xref: /openbmc/linux/drivers/counter/104-quad-8.c (revision 9830288a)
1f1d8a071SWilliam Breathitt Gray // SPDX-License-Identifier: GPL-2.0
2f1d8a071SWilliam Breathitt Gray /*
3f1d8a071SWilliam Breathitt Gray  * Counter driver for the ACCES 104-QUAD-8
4f1d8a071SWilliam Breathitt Gray  * Copyright (C) 2016 William Breathitt Gray
5f1d8a071SWilliam Breathitt Gray  *
6f1d8a071SWilliam Breathitt Gray  * This driver supports the ACCES 104-QUAD-8 and ACCES 104-QUAD-4.
7f1d8a071SWilliam Breathitt Gray  */
8f1d8a071SWilliam Breathitt Gray #include <linux/bitops.h>
9f1d8a071SWilliam Breathitt Gray #include <linux/counter.h>
10f1d8a071SWilliam Breathitt Gray #include <linux/device.h>
11f1d8a071SWilliam Breathitt Gray #include <linux/errno.h>
12f1d8a071SWilliam Breathitt Gray #include <linux/io.h>
13f1d8a071SWilliam Breathitt Gray #include <linux/ioport.h>
147aa2ba0dSWilliam Breathitt Gray #include <linux/interrupt.h>
15f1d8a071SWilliam Breathitt Gray #include <linux/isa.h>
16f1d8a071SWilliam Breathitt Gray #include <linux/kernel.h>
17c95cc0d9SWilliam Breathitt Gray #include <linux/list.h>
18f1d8a071SWilliam Breathitt Gray #include <linux/module.h>
19f1d8a071SWilliam Breathitt Gray #include <linux/moduleparam.h>
20f1d8a071SWilliam Breathitt Gray #include <linux/types.h>
2109db4678SWilliam Breathitt Gray #include <linux/spinlock.h>
22f1d8a071SWilliam Breathitt Gray 
23f1d8a071SWilliam Breathitt Gray #define QUAD8_EXTENT 32
24f1d8a071SWilliam Breathitt Gray 
25f1d8a071SWilliam Breathitt Gray static unsigned int base[max_num_isa_dev(QUAD8_EXTENT)];
26f1d8a071SWilliam Breathitt Gray static unsigned int num_quad8;
27af383bb1SWilliam Breathitt Gray module_param_hw_array(base, uint, ioport, &num_quad8, 0);
28f1d8a071SWilliam Breathitt Gray MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
29f1d8a071SWilliam Breathitt Gray 
307aa2ba0dSWilliam Breathitt Gray static unsigned int irq[max_num_isa_dev(QUAD8_EXTENT)];
317aa2ba0dSWilliam Breathitt Gray module_param_hw_array(irq, uint, irq, NULL, 0);
327aa2ba0dSWilliam Breathitt Gray MODULE_PARM_DESC(irq, "ACCES 104-QUAD-8 interrupt line numbers");
337aa2ba0dSWilliam Breathitt Gray 
34f1d8a071SWilliam Breathitt Gray #define QUAD8_NUM_COUNTERS 8
35f1d8a071SWilliam Breathitt Gray 
36f1d8a071SWilliam Breathitt Gray /**
37daae1ee5SWilliam Breathitt Gray  * struct channel_reg - channel register structure
38daae1ee5SWilliam Breathitt Gray  * @data:	Count data
39daae1ee5SWilliam Breathitt Gray  * @control:	Channel flags and control
40daae1ee5SWilliam Breathitt Gray  */
41daae1ee5SWilliam Breathitt Gray struct channel_reg {
42daae1ee5SWilliam Breathitt Gray 	u8 data;
43daae1ee5SWilliam Breathitt Gray 	u8 control;
44daae1ee5SWilliam Breathitt Gray };
45daae1ee5SWilliam Breathitt Gray 
46daae1ee5SWilliam Breathitt Gray /**
47daae1ee5SWilliam Breathitt Gray  * struct quad8_reg - device register structure
48daae1ee5SWilliam Breathitt Gray  * @channel:		quadrature counter data and control
49daae1ee5SWilliam Breathitt Gray  * @interrupt_status:	channel interrupt status
50daae1ee5SWilliam Breathitt Gray  * @channel_oper:	enable/reset counters and interrupt functions
51daae1ee5SWilliam Breathitt Gray  * @index_interrupt:	enable channel interrupts
52daae1ee5SWilliam Breathitt Gray  * @reserved:		reserved for Factory Use
53daae1ee5SWilliam Breathitt Gray  * @index_input_levels:	index signal logical input level
54daae1ee5SWilliam Breathitt Gray  * @cable_status:	differential encoder cable status
55daae1ee5SWilliam Breathitt Gray  */
56daae1ee5SWilliam Breathitt Gray struct quad8_reg {
57daae1ee5SWilliam Breathitt Gray 	struct channel_reg channel[QUAD8_NUM_COUNTERS];
58daae1ee5SWilliam Breathitt Gray 	u8 interrupt_status;
59daae1ee5SWilliam Breathitt Gray 	u8 channel_oper;
60daae1ee5SWilliam Breathitt Gray 	u8 index_interrupt;
61daae1ee5SWilliam Breathitt Gray 	u8 reserved[3];
62daae1ee5SWilliam Breathitt Gray 	u8 index_input_levels;
63daae1ee5SWilliam Breathitt Gray 	u8 cable_status;
64daae1ee5SWilliam Breathitt Gray };
65daae1ee5SWilliam Breathitt Gray 
66daae1ee5SWilliam Breathitt Gray /**
67e357e81fSWilliam Breathitt Gray  * struct quad8 - device private data structure
6894a853ecSWilliam Breathitt Gray  * @lock:		lock to prevent clobbering device states during R/W ops
69f1d8a071SWilliam Breathitt Gray  * @counter:		instance of the counter_device
70954ab5ccSWilliam Breathitt Gray  * @fck_prescaler:	array of filter clock prescaler configurations
71f1d8a071SWilliam Breathitt Gray  * @preset:		array of preset values
72f1d8a071SWilliam Breathitt Gray  * @count_mode:		array of count mode configurations
73f1d8a071SWilliam Breathitt Gray  * @quadrature_mode:	array of quadrature mode configurations
74f1d8a071SWilliam Breathitt Gray  * @quadrature_scale:	array of quadrature mode scale configurations
75f1d8a071SWilliam Breathitt Gray  * @ab_enable:		array of A and B inputs enable configurations
76f1d8a071SWilliam Breathitt Gray  * @preset_enable:	array of set_to_preset_on_index attribute configurations
777aa2ba0dSWilliam Breathitt Gray  * @irq_trigger:	array of current IRQ trigger function configurations
78f1d8a071SWilliam Breathitt Gray  * @synchronous_mode:	array of index function synchronous mode configurations
79f1d8a071SWilliam Breathitt Gray  * @index_polarity:	array of index function polarity configurations
80954ab5ccSWilliam Breathitt Gray  * @cable_fault_enable:	differential encoder cable status enable configurations
81daae1ee5SWilliam Breathitt Gray  * @reg:		I/O address offset for the device registers
82f1d8a071SWilliam Breathitt Gray  */
83e357e81fSWilliam Breathitt Gray struct quad8 {
8409db4678SWilliam Breathitt Gray 	spinlock_t lock;
85de65d055SWilliam Breathitt Gray 	unsigned int fck_prescaler[QUAD8_NUM_COUNTERS];
86f1d8a071SWilliam Breathitt Gray 	unsigned int preset[QUAD8_NUM_COUNTERS];
87f1d8a071SWilliam Breathitt Gray 	unsigned int count_mode[QUAD8_NUM_COUNTERS];
88f1d8a071SWilliam Breathitt Gray 	unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
89f1d8a071SWilliam Breathitt Gray 	unsigned int quadrature_scale[QUAD8_NUM_COUNTERS];
90f1d8a071SWilliam Breathitt Gray 	unsigned int ab_enable[QUAD8_NUM_COUNTERS];
91f1d8a071SWilliam Breathitt Gray 	unsigned int preset_enable[QUAD8_NUM_COUNTERS];
927aa2ba0dSWilliam Breathitt Gray 	unsigned int irq_trigger[QUAD8_NUM_COUNTERS];
93f1d8a071SWilliam Breathitt Gray 	unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
94f1d8a071SWilliam Breathitt Gray 	unsigned int index_polarity[QUAD8_NUM_COUNTERS];
95954ab5ccSWilliam Breathitt Gray 	unsigned int cable_fault_enable;
96daae1ee5SWilliam Breathitt Gray 	struct quad8_reg __iomem *reg;
97f1d8a071SWilliam Breathitt Gray };
98f1d8a071SWilliam Breathitt Gray 
99f1d8a071SWilliam Breathitt Gray /* Borrow Toggle flip-flop */
100f1d8a071SWilliam Breathitt Gray #define QUAD8_FLAG_BT BIT(0)
101f1d8a071SWilliam Breathitt Gray /* Carry Toggle flip-flop */
102f1d8a071SWilliam Breathitt Gray #define QUAD8_FLAG_CT BIT(1)
103f1d8a071SWilliam Breathitt Gray /* Error flag */
104f1d8a071SWilliam Breathitt Gray #define QUAD8_FLAG_E BIT(4)
105f1d8a071SWilliam Breathitt Gray /* Up/Down flag */
106f1d8a071SWilliam Breathitt Gray #define QUAD8_FLAG_UD BIT(5)
107f1d8a071SWilliam Breathitt Gray /* Reset and Load Signal Decoders */
108f1d8a071SWilliam Breathitt Gray #define QUAD8_CTR_RLD 0x00
109f1d8a071SWilliam Breathitt Gray /* Counter Mode Register */
110f1d8a071SWilliam Breathitt Gray #define QUAD8_CTR_CMR 0x20
111f1d8a071SWilliam Breathitt Gray /* Input / Output Control Register */
112f1d8a071SWilliam Breathitt Gray #define QUAD8_CTR_IOR 0x40
113f1d8a071SWilliam Breathitt Gray /* Index Control Register */
114f1d8a071SWilliam Breathitt Gray #define QUAD8_CTR_IDR 0x60
115f1d8a071SWilliam Breathitt Gray /* Reset Byte Pointer (three byte data pointer) */
116f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_RESET_BP 0x01
117f1d8a071SWilliam Breathitt Gray /* Reset Counter */
118f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_RESET_CNTR 0x02
119f1d8a071SWilliam Breathitt Gray /* Reset Borrow Toggle, Carry Toggle, Compare Toggle, and Sign flags */
120f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_RESET_FLAGS 0x04
121f1d8a071SWilliam Breathitt Gray /* Reset Error flag */
122f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_RESET_E 0x06
123f1d8a071SWilliam Breathitt Gray /* Preset Register to Counter */
124f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_PRESET_CNTR 0x08
125f1d8a071SWilliam Breathitt Gray /* Transfer Counter to Output Latch */
126f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_CNTR_OUT 0x10
127de65d055SWilliam Breathitt Gray /* Transfer Preset Register LSB to FCK Prescaler */
128de65d055SWilliam Breathitt Gray #define QUAD8_RLD_PRESET_PSC 0x18
129f1d8a071SWilliam Breathitt Gray #define QUAD8_CHAN_OP_RESET_COUNTERS 0x01
1307aa2ba0dSWilliam Breathitt Gray #define QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC 0x04
131f1d8a071SWilliam Breathitt Gray #define QUAD8_CMR_QUADRATURE_X1 0x08
132f1d8a071SWilliam Breathitt Gray #define QUAD8_CMR_QUADRATURE_X2 0x10
133f1d8a071SWilliam Breathitt Gray #define QUAD8_CMR_QUADRATURE_X4 0x18
134f1d8a071SWilliam Breathitt Gray 
135f1d8a071SWilliam Breathitt Gray static int quad8_signal_read(struct counter_device *counter,
136493b938aSWilliam Breathitt Gray 			     struct counter_signal *signal,
137493b938aSWilliam Breathitt Gray 			     enum counter_signal_level *level)
138f1d8a071SWilliam Breathitt Gray {
139aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
140f1d8a071SWilliam Breathitt Gray 	unsigned int state;
141f1d8a071SWilliam Breathitt Gray 
142f1d8a071SWilliam Breathitt Gray 	/* Only Index signal levels can be read */
143f1d8a071SWilliam Breathitt Gray 	if (signal->id < 16)
144f1d8a071SWilliam Breathitt Gray 		return -EINVAL;
145f1d8a071SWilliam Breathitt Gray 
146daae1ee5SWilliam Breathitt Gray 	state = ioread8(&priv->reg->index_input_levels) & BIT(signal->id - 16);
147f1d8a071SWilliam Breathitt Gray 
148493b938aSWilliam Breathitt Gray 	*level = (state) ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW;
149f1d8a071SWilliam Breathitt Gray 
150f1d8a071SWilliam Breathitt Gray 	return 0;
151f1d8a071SWilliam Breathitt Gray }
152f1d8a071SWilliam Breathitt Gray 
153f1d8a071SWilliam Breathitt Gray static int quad8_count_read(struct counter_device *counter,
154aaec1a0fSWilliam Breathitt Gray 			    struct counter_count *count, u64 *val)
155f1d8a071SWilliam Breathitt Gray {
156aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
157daae1ee5SWilliam Breathitt Gray 	struct channel_reg __iomem *const chan = priv->reg->channel + count->id;
158f1d8a071SWilliam Breathitt Gray 	unsigned int flags;
159f1d8a071SWilliam Breathitt Gray 	unsigned int borrow;
160f1d8a071SWilliam Breathitt Gray 	unsigned int carry;
16109db4678SWilliam Breathitt Gray 	unsigned long irqflags;
162f1d8a071SWilliam Breathitt Gray 	int i;
163f1d8a071SWilliam Breathitt Gray 
164daae1ee5SWilliam Breathitt Gray 	flags = ioread8(&chan->control);
165f1d8a071SWilliam Breathitt Gray 	borrow = flags & QUAD8_FLAG_BT;
166f1d8a071SWilliam Breathitt Gray 	carry = !!(flags & QUAD8_FLAG_CT);
167f1d8a071SWilliam Breathitt Gray 
168f1d8a071SWilliam Breathitt Gray 	/* Borrow XOR Carry effectively doubles count range */
169d49e6ee2SWilliam Breathitt Gray 	*val = (unsigned long)(borrow ^ carry) << 24;
170f1d8a071SWilliam Breathitt Gray 
17109db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
172fc069262SSyed Nayyar Waris 
173f1d8a071SWilliam Breathitt Gray 	/* Reset Byte Pointer; transfer Counter to Output Latch */
174b6e9cdedSWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
175daae1ee5SWilliam Breathitt Gray 		 &chan->control);
176f1d8a071SWilliam Breathitt Gray 
177f1d8a071SWilliam Breathitt Gray 	for (i = 0; i < 3; i++)
178daae1ee5SWilliam Breathitt Gray 		*val |= (unsigned long)ioread8(&chan->data) << (8 * i);
179f1d8a071SWilliam Breathitt Gray 
18009db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
181fc069262SSyed Nayyar Waris 
182f1d8a071SWilliam Breathitt Gray 	return 0;
183f1d8a071SWilliam Breathitt Gray }
184f1d8a071SWilliam Breathitt Gray 
185f1d8a071SWilliam Breathitt Gray static int quad8_count_write(struct counter_device *counter,
186aaec1a0fSWilliam Breathitt Gray 			     struct counter_count *count, u64 val)
187f1d8a071SWilliam Breathitt Gray {
188aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
189daae1ee5SWilliam Breathitt Gray 	struct channel_reg __iomem *const chan = priv->reg->channel + count->id;
19009db4678SWilliam Breathitt Gray 	unsigned long irqflags;
191f1d8a071SWilliam Breathitt Gray 	int i;
192f1d8a071SWilliam Breathitt Gray 
193f1d8a071SWilliam Breathitt Gray 	/* Only 24-bit values are supported */
194d49e6ee2SWilliam Breathitt Gray 	if (val > 0xFFFFFF)
195e2ff3198SWilliam Breathitt Gray 		return -ERANGE;
196f1d8a071SWilliam Breathitt Gray 
19709db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
198fc069262SSyed Nayyar Waris 
199f1d8a071SWilliam Breathitt Gray 	/* Reset Byte Pointer */
200daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, &chan->control);
201f1d8a071SWilliam Breathitt Gray 
202f1d8a071SWilliam Breathitt Gray 	/* Counter can only be set via Preset Register */
203f1d8a071SWilliam Breathitt Gray 	for (i = 0; i < 3; i++)
204daae1ee5SWilliam Breathitt Gray 		iowrite8(val >> (8 * i), &chan->data);
205f1d8a071SWilliam Breathitt Gray 
206f1d8a071SWilliam Breathitt Gray 	/* Transfer Preset Register to Counter */
207daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_PRESET_CNTR, &chan->control);
208f1d8a071SWilliam Breathitt Gray 
209f1d8a071SWilliam Breathitt Gray 	/* Reset Byte Pointer */
210daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, &chan->control);
211f1d8a071SWilliam Breathitt Gray 
212f1d8a071SWilliam Breathitt Gray 	/* Set Preset Register back to original value */
213d49e6ee2SWilliam Breathitt Gray 	val = priv->preset[count->id];
214f1d8a071SWilliam Breathitt Gray 	for (i = 0; i < 3; i++)
215daae1ee5SWilliam Breathitt Gray 		iowrite8(val >> (8 * i), &chan->data);
216f1d8a071SWilliam Breathitt Gray 
217f1d8a071SWilliam Breathitt Gray 	/* Reset Borrow, Carry, Compare, and Sign flags */
218daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, &chan->control);
219f1d8a071SWilliam Breathitt Gray 	/* Reset Error flag */
220daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, &chan->control);
221f1d8a071SWilliam Breathitt Gray 
22209db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
223fc069262SSyed Nayyar Waris 
224f1d8a071SWilliam Breathitt Gray 	return 0;
225f1d8a071SWilliam Breathitt Gray }
226f1d8a071SWilliam Breathitt Gray 
227394a0150SWilliam Breathitt Gray static const enum counter_function quad8_count_functions_list[] = {
228aaec1a0fSWilliam Breathitt Gray 	COUNTER_FUNCTION_PULSE_DIRECTION,
229aaec1a0fSWilliam Breathitt Gray 	COUNTER_FUNCTION_QUADRATURE_X1_A,
230aaec1a0fSWilliam Breathitt Gray 	COUNTER_FUNCTION_QUADRATURE_X2_A,
231aaec1a0fSWilliam Breathitt Gray 	COUNTER_FUNCTION_QUADRATURE_X4,
232f1d8a071SWilliam Breathitt Gray };
233f1d8a071SWilliam Breathitt Gray 
234aaec1a0fSWilliam Breathitt Gray static int quad8_function_read(struct counter_device *counter,
235aaec1a0fSWilliam Breathitt Gray 			       struct counter_count *count,
236aaec1a0fSWilliam Breathitt Gray 			       enum counter_function *function)
237f1d8a071SWilliam Breathitt Gray {
238aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
239f1d8a071SWilliam Breathitt Gray 	const int id = count->id;
24009db4678SWilliam Breathitt Gray 	unsigned long irqflags;
241f1d8a071SWilliam Breathitt Gray 
24209db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
243fc069262SSyed Nayyar Waris 
244fc069262SSyed Nayyar Waris 	if (priv->quadrature_mode[id])
245fc069262SSyed Nayyar Waris 		switch (priv->quadrature_scale[id]) {
246f1d8a071SWilliam Breathitt Gray 		case 0:
247aaec1a0fSWilliam Breathitt Gray 			*function = COUNTER_FUNCTION_QUADRATURE_X1_A;
248f1d8a071SWilliam Breathitt Gray 			break;
249f1d8a071SWilliam Breathitt Gray 		case 1:
250aaec1a0fSWilliam Breathitt Gray 			*function = COUNTER_FUNCTION_QUADRATURE_X2_A;
251f1d8a071SWilliam Breathitt Gray 			break;
252f1d8a071SWilliam Breathitt Gray 		case 2:
253aaec1a0fSWilliam Breathitt Gray 			*function = COUNTER_FUNCTION_QUADRATURE_X4;
254f1d8a071SWilliam Breathitt Gray 			break;
255f1d8a071SWilliam Breathitt Gray 		}
256f1d8a071SWilliam Breathitt Gray 	else
257aaec1a0fSWilliam Breathitt Gray 		*function = COUNTER_FUNCTION_PULSE_DIRECTION;
258f1d8a071SWilliam Breathitt Gray 
25909db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
260fc069262SSyed Nayyar Waris 
261f1d8a071SWilliam Breathitt Gray 	return 0;
262f1d8a071SWilliam Breathitt Gray }
263f1d8a071SWilliam Breathitt Gray 
264aaec1a0fSWilliam Breathitt Gray static int quad8_function_write(struct counter_device *counter,
265aaec1a0fSWilliam Breathitt Gray 				struct counter_count *count,
266aaec1a0fSWilliam Breathitt Gray 				enum counter_function function)
267f1d8a071SWilliam Breathitt Gray {
268aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
269f1d8a071SWilliam Breathitt Gray 	const int id = count->id;
270f1d8a071SWilliam Breathitt Gray 	unsigned int *const quadrature_mode = priv->quadrature_mode + id;
271f1d8a071SWilliam Breathitt Gray 	unsigned int *const scale = priv->quadrature_scale + id;
272f1d8a071SWilliam Breathitt Gray 	unsigned int *const synchronous_mode = priv->synchronous_mode + id;
273daae1ee5SWilliam Breathitt Gray 	u8 __iomem *const control = &priv->reg->channel[id].control;
27409db4678SWilliam Breathitt Gray 	unsigned long irqflags;
275fc069262SSyed Nayyar Waris 	unsigned int mode_cfg;
276fc069262SSyed Nayyar Waris 	unsigned int idr_cfg;
277fc069262SSyed Nayyar Waris 
27809db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
279fc069262SSyed Nayyar Waris 
280fc069262SSyed Nayyar Waris 	mode_cfg = priv->count_mode[id] << 1;
281fc069262SSyed Nayyar Waris 	idr_cfg = priv->index_polarity[id] << 1;
282f1d8a071SWilliam Breathitt Gray 
283aaec1a0fSWilliam Breathitt Gray 	if (function == COUNTER_FUNCTION_PULSE_DIRECTION) {
284f1d8a071SWilliam Breathitt Gray 		*quadrature_mode = 0;
285f1d8a071SWilliam Breathitt Gray 
286f1d8a071SWilliam Breathitt Gray 		/* Quadrature scaling only available in quadrature mode */
287f1d8a071SWilliam Breathitt Gray 		*scale = 0;
288f1d8a071SWilliam Breathitt Gray 
289f1d8a071SWilliam Breathitt Gray 		/* Synchronous function not supported in non-quadrature mode */
290f1d8a071SWilliam Breathitt Gray 		if (*synchronous_mode) {
291f1d8a071SWilliam Breathitt Gray 			*synchronous_mode = 0;
292f1d8a071SWilliam Breathitt Gray 			/* Disable synchronous function mode */
293daae1ee5SWilliam Breathitt Gray 			iowrite8(QUAD8_CTR_IDR | idr_cfg, control);
294f1d8a071SWilliam Breathitt Gray 		}
295f1d8a071SWilliam Breathitt Gray 	} else {
296f1d8a071SWilliam Breathitt Gray 		*quadrature_mode = 1;
297f1d8a071SWilliam Breathitt Gray 
298f1d8a071SWilliam Breathitt Gray 		switch (function) {
299aaec1a0fSWilliam Breathitt Gray 		case COUNTER_FUNCTION_QUADRATURE_X1_A:
300f1d8a071SWilliam Breathitt Gray 			*scale = 0;
301f1d8a071SWilliam Breathitt Gray 			mode_cfg |= QUAD8_CMR_QUADRATURE_X1;
302f1d8a071SWilliam Breathitt Gray 			break;
303aaec1a0fSWilliam Breathitt Gray 		case COUNTER_FUNCTION_QUADRATURE_X2_A:
304f1d8a071SWilliam Breathitt Gray 			*scale = 1;
305f1d8a071SWilliam Breathitt Gray 			mode_cfg |= QUAD8_CMR_QUADRATURE_X2;
306f1d8a071SWilliam Breathitt Gray 			break;
307aaec1a0fSWilliam Breathitt Gray 		case COUNTER_FUNCTION_QUADRATURE_X4:
308f1d8a071SWilliam Breathitt Gray 			*scale = 2;
309f1d8a071SWilliam Breathitt Gray 			mode_cfg |= QUAD8_CMR_QUADRATURE_X4;
310f1d8a071SWilliam Breathitt Gray 			break;
311b11eed15SWilliam Breathitt Gray 		default:
312b11eed15SWilliam Breathitt Gray 			/* should never reach this path */
31309db4678SWilliam Breathitt Gray 			spin_unlock_irqrestore(&priv->lock, irqflags);
314b11eed15SWilliam Breathitt Gray 			return -EINVAL;
315f1d8a071SWilliam Breathitt Gray 		}
316f1d8a071SWilliam Breathitt Gray 	}
317f1d8a071SWilliam Breathitt Gray 
318f1d8a071SWilliam Breathitt Gray 	/* Load mode configuration to Counter Mode Register */
319daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_CMR | mode_cfg, control);
320f1d8a071SWilliam Breathitt Gray 
32109db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
322fc069262SSyed Nayyar Waris 
323f1d8a071SWilliam Breathitt Gray 	return 0;
324f1d8a071SWilliam Breathitt Gray }
325f1d8a071SWilliam Breathitt Gray 
326aaec1a0fSWilliam Breathitt Gray static int quad8_direction_read(struct counter_device *counter,
327aaec1a0fSWilliam Breathitt Gray 				struct counter_count *count,
328aaec1a0fSWilliam Breathitt Gray 				enum counter_count_direction *direction)
329f1d8a071SWilliam Breathitt Gray {
330aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
331f1d8a071SWilliam Breathitt Gray 	unsigned int ud_flag;
332daae1ee5SWilliam Breathitt Gray 	u8 __iomem *const flag_addr = &priv->reg->channel[count->id].control;
333f1d8a071SWilliam Breathitt Gray 
334f1d8a071SWilliam Breathitt Gray 	/* U/D flag: nonzero = up, zero = down */
335b6e9cdedSWilliam Breathitt Gray 	ud_flag = ioread8(flag_addr) & QUAD8_FLAG_UD;
336f1d8a071SWilliam Breathitt Gray 
337f1d8a071SWilliam Breathitt Gray 	*direction = (ud_flag) ? COUNTER_COUNT_DIRECTION_FORWARD :
338f1d8a071SWilliam Breathitt Gray 		COUNTER_COUNT_DIRECTION_BACKWARD;
339aaec1a0fSWilliam Breathitt Gray 
340aaec1a0fSWilliam Breathitt Gray 	return 0;
341f1d8a071SWilliam Breathitt Gray }
342f1d8a071SWilliam Breathitt Gray 
3436a9eb0e3SWilliam Breathitt Gray static const enum counter_synapse_action quad8_index_actions_list[] = {
344aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_NONE,
345aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
346f1d8a071SWilliam Breathitt Gray };
347f1d8a071SWilliam Breathitt Gray 
3486a9eb0e3SWilliam Breathitt Gray static const enum counter_synapse_action quad8_synapse_actions_list[] = {
349aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_NONE,
350aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
351aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
352aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
353f1d8a071SWilliam Breathitt Gray };
354f1d8a071SWilliam Breathitt Gray 
355aaec1a0fSWilliam Breathitt Gray static int quad8_action_read(struct counter_device *counter,
356aaec1a0fSWilliam Breathitt Gray 			     struct counter_count *count,
357aaec1a0fSWilliam Breathitt Gray 			     struct counter_synapse *synapse,
358aaec1a0fSWilliam Breathitt Gray 			     enum counter_synapse_action *action)
359f1d8a071SWilliam Breathitt Gray {
360aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
361f1d8a071SWilliam Breathitt Gray 	int err;
362aaec1a0fSWilliam Breathitt Gray 	enum counter_function function;
363f1d8a071SWilliam Breathitt Gray 	const size_t signal_a_id = count->synapses[0].signal->id;
364f1d8a071SWilliam Breathitt Gray 	enum counter_count_direction direction;
365f1d8a071SWilliam Breathitt Gray 
366f1d8a071SWilliam Breathitt Gray 	/* Handle Index signals */
367f1d8a071SWilliam Breathitt Gray 	if (synapse->signal->id >= 16) {
368f1d8a071SWilliam Breathitt Gray 		if (priv->preset_enable[count->id])
369aaec1a0fSWilliam Breathitt Gray 			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
370f1d8a071SWilliam Breathitt Gray 		else
371aaec1a0fSWilliam Breathitt Gray 			*action = COUNTER_SYNAPSE_ACTION_NONE;
372f1d8a071SWilliam Breathitt Gray 
373f1d8a071SWilliam Breathitt Gray 		return 0;
374f1d8a071SWilliam Breathitt Gray 	}
375f1d8a071SWilliam Breathitt Gray 
376aaec1a0fSWilliam Breathitt Gray 	err = quad8_function_read(counter, count, &function);
377f1d8a071SWilliam Breathitt Gray 	if (err)
378f1d8a071SWilliam Breathitt Gray 		return err;
379f1d8a071SWilliam Breathitt Gray 
380f1d8a071SWilliam Breathitt Gray 	/* Default action mode */
381aaec1a0fSWilliam Breathitt Gray 	*action = COUNTER_SYNAPSE_ACTION_NONE;
382f1d8a071SWilliam Breathitt Gray 
383f1d8a071SWilliam Breathitt Gray 	/* Determine action mode based on current count function mode */
384f1d8a071SWilliam Breathitt Gray 	switch (function) {
385aaec1a0fSWilliam Breathitt Gray 	case COUNTER_FUNCTION_PULSE_DIRECTION:
386f1d8a071SWilliam Breathitt Gray 		if (synapse->signal->id == signal_a_id)
387aaec1a0fSWilliam Breathitt Gray 			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
388b11eed15SWilliam Breathitt Gray 		return 0;
389aaec1a0fSWilliam Breathitt Gray 	case COUNTER_FUNCTION_QUADRATURE_X1_A:
390f1d8a071SWilliam Breathitt Gray 		if (synapse->signal->id == signal_a_id) {
391aaec1a0fSWilliam Breathitt Gray 			err = quad8_direction_read(counter, count, &direction);
392aaec1a0fSWilliam Breathitt Gray 			if (err)
393aaec1a0fSWilliam Breathitt Gray 				return err;
394f1d8a071SWilliam Breathitt Gray 
395f1d8a071SWilliam Breathitt Gray 			if (direction == COUNTER_COUNT_DIRECTION_FORWARD)
396aaec1a0fSWilliam Breathitt Gray 				*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
397f1d8a071SWilliam Breathitt Gray 			else
398aaec1a0fSWilliam Breathitt Gray 				*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
399f1d8a071SWilliam Breathitt Gray 		}
400b11eed15SWilliam Breathitt Gray 		return 0;
401aaec1a0fSWilliam Breathitt Gray 	case COUNTER_FUNCTION_QUADRATURE_X2_A:
402f1d8a071SWilliam Breathitt Gray 		if (synapse->signal->id == signal_a_id)
403aaec1a0fSWilliam Breathitt Gray 			*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
404b11eed15SWilliam Breathitt Gray 		return 0;
405aaec1a0fSWilliam Breathitt Gray 	case COUNTER_FUNCTION_QUADRATURE_X4:
406aaec1a0fSWilliam Breathitt Gray 		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
407f1d8a071SWilliam Breathitt Gray 		return 0;
408b11eed15SWilliam Breathitt Gray 	default:
409b11eed15SWilliam Breathitt Gray 		/* should never reach this path */
410b11eed15SWilliam Breathitt Gray 		return -EINVAL;
411b11eed15SWilliam Breathitt Gray 	}
412f1d8a071SWilliam Breathitt Gray }
413f1d8a071SWilliam Breathitt Gray 
4147aa2ba0dSWilliam Breathitt Gray enum {
4157aa2ba0dSWilliam Breathitt Gray 	QUAD8_EVENT_CARRY = 0,
4167aa2ba0dSWilliam Breathitt Gray 	QUAD8_EVENT_COMPARE = 1,
4177aa2ba0dSWilliam Breathitt Gray 	QUAD8_EVENT_CARRY_BORROW = 2,
4187aa2ba0dSWilliam Breathitt Gray 	QUAD8_EVENT_INDEX = 3,
4197aa2ba0dSWilliam Breathitt Gray };
4207aa2ba0dSWilliam Breathitt Gray 
4217aa2ba0dSWilliam Breathitt Gray static int quad8_events_configure(struct counter_device *counter)
4227aa2ba0dSWilliam Breathitt Gray {
423aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
4247aa2ba0dSWilliam Breathitt Gray 	unsigned long irq_enabled = 0;
4257aa2ba0dSWilliam Breathitt Gray 	unsigned long irqflags;
426c95cc0d9SWilliam Breathitt Gray 	struct counter_event_node *event_node;
427c95cc0d9SWilliam Breathitt Gray 	unsigned int next_irq_trigger;
4287aa2ba0dSWilliam Breathitt Gray 	unsigned long ior_cfg;
4297aa2ba0dSWilliam Breathitt Gray 
4307aa2ba0dSWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
4317aa2ba0dSWilliam Breathitt Gray 
432c95cc0d9SWilliam Breathitt Gray 	list_for_each_entry(event_node, &counter->events_list, l) {
433c95cc0d9SWilliam Breathitt Gray 		switch (event_node->event) {
434c95cc0d9SWilliam Breathitt Gray 		case COUNTER_EVENT_OVERFLOW:
435c95cc0d9SWilliam Breathitt Gray 			next_irq_trigger = QUAD8_EVENT_CARRY;
436c95cc0d9SWilliam Breathitt Gray 			break;
437c95cc0d9SWilliam Breathitt Gray 		case COUNTER_EVENT_THRESHOLD:
438c95cc0d9SWilliam Breathitt Gray 			next_irq_trigger = QUAD8_EVENT_COMPARE;
439c95cc0d9SWilliam Breathitt Gray 			break;
440c95cc0d9SWilliam Breathitt Gray 		case COUNTER_EVENT_OVERFLOW_UNDERFLOW:
441c95cc0d9SWilliam Breathitt Gray 			next_irq_trigger = QUAD8_EVENT_CARRY_BORROW;
442c95cc0d9SWilliam Breathitt Gray 			break;
443c95cc0d9SWilliam Breathitt Gray 		case COUNTER_EVENT_INDEX:
444c95cc0d9SWilliam Breathitt Gray 			next_irq_trigger = QUAD8_EVENT_INDEX;
445c95cc0d9SWilliam Breathitt Gray 			break;
446c95cc0d9SWilliam Breathitt Gray 		default:
447c95cc0d9SWilliam Breathitt Gray 			/* should never reach this path */
448c95cc0d9SWilliam Breathitt Gray 			spin_unlock_irqrestore(&priv->lock, irqflags);
449c95cc0d9SWilliam Breathitt Gray 			return -EINVAL;
4507aa2ba0dSWilliam Breathitt Gray 		}
4517aa2ba0dSWilliam Breathitt Gray 
452c95cc0d9SWilliam Breathitt Gray 		/* Skip configuration if it is the same as previously set */
453c95cc0d9SWilliam Breathitt Gray 		if (priv->irq_trigger[event_node->channel] == next_irq_trigger)
454c95cc0d9SWilliam Breathitt Gray 			continue;
455c95cc0d9SWilliam Breathitt Gray 
456c95cc0d9SWilliam Breathitt Gray 		/* Save new IRQ function configuration */
457c95cc0d9SWilliam Breathitt Gray 		priv->irq_trigger[event_node->channel] = next_irq_trigger;
458c95cc0d9SWilliam Breathitt Gray 
459c95cc0d9SWilliam Breathitt Gray 		/* Load configuration to I/O Control Register */
460c95cc0d9SWilliam Breathitt Gray 		ior_cfg = priv->ab_enable[event_node->channel] |
461c95cc0d9SWilliam Breathitt Gray 			  priv->preset_enable[event_node->channel] << 1 |
462c95cc0d9SWilliam Breathitt Gray 			  priv->irq_trigger[event_node->channel] << 3;
463daae1ee5SWilliam Breathitt Gray 		iowrite8(QUAD8_CTR_IOR | ior_cfg,
464daae1ee5SWilliam Breathitt Gray 			 &priv->reg->channel[event_node->channel].control);
4657aa2ba0dSWilliam Breathitt Gray 
4667aa2ba0dSWilliam Breathitt Gray 		/* Enable IRQ line */
467c95cc0d9SWilliam Breathitt Gray 		irq_enabled |= BIT(event_node->channel);
4687aa2ba0dSWilliam Breathitt Gray 	}
4697aa2ba0dSWilliam Breathitt Gray 
470daae1ee5SWilliam Breathitt Gray 	iowrite8(irq_enabled, &priv->reg->index_interrupt);
4717aa2ba0dSWilliam Breathitt Gray 
4727aa2ba0dSWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
4737aa2ba0dSWilliam Breathitt Gray 
4747aa2ba0dSWilliam Breathitt Gray 	return 0;
4757aa2ba0dSWilliam Breathitt Gray }
4767aa2ba0dSWilliam Breathitt Gray 
4777aa2ba0dSWilliam Breathitt Gray static int quad8_watch_validate(struct counter_device *counter,
4787aa2ba0dSWilliam Breathitt Gray 				const struct counter_watch *watch)
4797aa2ba0dSWilliam Breathitt Gray {
480c95cc0d9SWilliam Breathitt Gray 	struct counter_event_node *event_node;
4817aa2ba0dSWilliam Breathitt Gray 
4827aa2ba0dSWilliam Breathitt Gray 	if (watch->channel > QUAD8_NUM_COUNTERS - 1)
4837aa2ba0dSWilliam Breathitt Gray 		return -EINVAL;
4847aa2ba0dSWilliam Breathitt Gray 
4857aa2ba0dSWilliam Breathitt Gray 	switch (watch->event) {
4867aa2ba0dSWilliam Breathitt Gray 	case COUNTER_EVENT_OVERFLOW:
4877aa2ba0dSWilliam Breathitt Gray 	case COUNTER_EVENT_THRESHOLD:
4887aa2ba0dSWilliam Breathitt Gray 	case COUNTER_EVENT_OVERFLOW_UNDERFLOW:
4897aa2ba0dSWilliam Breathitt Gray 	case COUNTER_EVENT_INDEX:
490c95cc0d9SWilliam Breathitt Gray 		list_for_each_entry(event_node, &counter->next_events_list, l)
491c95cc0d9SWilliam Breathitt Gray 			if (watch->channel == event_node->channel &&
492c95cc0d9SWilliam Breathitt Gray 				watch->event != event_node->event)
4937aa2ba0dSWilliam Breathitt Gray 				return -EINVAL;
4947aa2ba0dSWilliam Breathitt Gray 		return 0;
4957aa2ba0dSWilliam Breathitt Gray 	default:
4967aa2ba0dSWilliam Breathitt Gray 		return -EINVAL;
4977aa2ba0dSWilliam Breathitt Gray 	}
4987aa2ba0dSWilliam Breathitt Gray }
4997aa2ba0dSWilliam Breathitt Gray 
50017aa207eSYueHaibing static const struct counter_ops quad8_ops = {
501f1d8a071SWilliam Breathitt Gray 	.signal_read = quad8_signal_read,
502f1d8a071SWilliam Breathitt Gray 	.count_read = quad8_count_read,
503f1d8a071SWilliam Breathitt Gray 	.count_write = quad8_count_write,
504aaec1a0fSWilliam Breathitt Gray 	.function_read = quad8_function_read,
505aaec1a0fSWilliam Breathitt Gray 	.function_write = quad8_function_write,
5067aa2ba0dSWilliam Breathitt Gray 	.action_read = quad8_action_read,
5077aa2ba0dSWilliam Breathitt Gray 	.events_configure = quad8_events_configure,
5087aa2ba0dSWilliam Breathitt Gray 	.watch_validate = quad8_watch_validate,
509f1d8a071SWilliam Breathitt Gray };
510f1d8a071SWilliam Breathitt Gray 
511e357e81fSWilliam Breathitt Gray static const char *const quad8_index_polarity_modes[] = {
512e357e81fSWilliam Breathitt Gray 	"negative",
513e357e81fSWilliam Breathitt Gray 	"positive"
514e357e81fSWilliam Breathitt Gray };
515e357e81fSWilliam Breathitt Gray 
516f1d8a071SWilliam Breathitt Gray static int quad8_index_polarity_get(struct counter_device *counter,
517aaec1a0fSWilliam Breathitt Gray 				    struct counter_signal *signal,
518aaec1a0fSWilliam Breathitt Gray 				    u32 *index_polarity)
519f1d8a071SWilliam Breathitt Gray {
520aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
521f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
522f1d8a071SWilliam Breathitt Gray 
523f1d8a071SWilliam Breathitt Gray 	*index_polarity = priv->index_polarity[channel_id];
524f1d8a071SWilliam Breathitt Gray 
525f1d8a071SWilliam Breathitt Gray 	return 0;
526f1d8a071SWilliam Breathitt Gray }
527f1d8a071SWilliam Breathitt Gray 
528f1d8a071SWilliam Breathitt Gray static int quad8_index_polarity_set(struct counter_device *counter,
529aaec1a0fSWilliam Breathitt Gray 				    struct counter_signal *signal,
530aaec1a0fSWilliam Breathitt Gray 				    u32 index_polarity)
531f1d8a071SWilliam Breathitt Gray {
532aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
533f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
534daae1ee5SWilliam Breathitt Gray 	u8 __iomem *const control = &priv->reg->channel[channel_id].control;
53509db4678SWilliam Breathitt Gray 	unsigned long irqflags;
536fc069262SSyed Nayyar Waris 	unsigned int idr_cfg = index_polarity << 1;
537fc069262SSyed Nayyar Waris 
53809db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
539fc069262SSyed Nayyar Waris 
540fc069262SSyed Nayyar Waris 	idr_cfg |= priv->synchronous_mode[channel_id];
541f1d8a071SWilliam Breathitt Gray 
542f1d8a071SWilliam Breathitt Gray 	priv->index_polarity[channel_id] = index_polarity;
543f1d8a071SWilliam Breathitt Gray 
544f1d8a071SWilliam Breathitt Gray 	/* Load Index Control configuration to Index Control Register */
545daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_IDR | idr_cfg, control);
546f1d8a071SWilliam Breathitt Gray 
54709db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
548fc069262SSyed Nayyar Waris 
549f1d8a071SWilliam Breathitt Gray 	return 0;
550f1d8a071SWilliam Breathitt Gray }
551f1d8a071SWilliam Breathitt Gray 
552*9830288aSWilliam Breathitt Gray static int quad8_polarity_read(struct counter_device *counter,
553*9830288aSWilliam Breathitt Gray 			       struct counter_signal *signal,
554*9830288aSWilliam Breathitt Gray 			       enum counter_signal_polarity *polarity)
555*9830288aSWilliam Breathitt Gray {
556*9830288aSWilliam Breathitt Gray 	int err;
557*9830288aSWilliam Breathitt Gray 	u32 index_polarity;
558*9830288aSWilliam Breathitt Gray 
559*9830288aSWilliam Breathitt Gray 	err = quad8_index_polarity_get(counter, signal, &index_polarity);
560*9830288aSWilliam Breathitt Gray 	if (err)
561*9830288aSWilliam Breathitt Gray 		return err;
562*9830288aSWilliam Breathitt Gray 
563*9830288aSWilliam Breathitt Gray 	*polarity = (index_polarity) ? COUNTER_SIGNAL_POLARITY_POSITIVE :
564*9830288aSWilliam Breathitt Gray 		COUNTER_SIGNAL_POLARITY_NEGATIVE;
565*9830288aSWilliam Breathitt Gray 
566*9830288aSWilliam Breathitt Gray 	return 0;
567*9830288aSWilliam Breathitt Gray }
568*9830288aSWilliam Breathitt Gray 
569*9830288aSWilliam Breathitt Gray static int quad8_polarity_write(struct counter_device *counter,
570*9830288aSWilliam Breathitt Gray 				struct counter_signal *signal,
571*9830288aSWilliam Breathitt Gray 				enum counter_signal_polarity polarity)
572*9830288aSWilliam Breathitt Gray {
573*9830288aSWilliam Breathitt Gray 	const u32 pol = (polarity == COUNTER_SIGNAL_POLARITY_POSITIVE) ? 1 : 0;
574*9830288aSWilliam Breathitt Gray 
575*9830288aSWilliam Breathitt Gray 	return quad8_index_polarity_set(counter, signal, pol);
576*9830288aSWilliam Breathitt Gray }
577*9830288aSWilliam Breathitt Gray 
578e357e81fSWilliam Breathitt Gray static const char *const quad8_synchronous_modes[] = {
579e357e81fSWilliam Breathitt Gray 	"non-synchronous",
580e357e81fSWilliam Breathitt Gray 	"synchronous"
581e357e81fSWilliam Breathitt Gray };
582e357e81fSWilliam Breathitt Gray 
583f1d8a071SWilliam Breathitt Gray static int quad8_synchronous_mode_get(struct counter_device *counter,
584aaec1a0fSWilliam Breathitt Gray 				      struct counter_signal *signal,
585aaec1a0fSWilliam Breathitt Gray 				      u32 *synchronous_mode)
586f1d8a071SWilliam Breathitt Gray {
587aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
588f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
589f1d8a071SWilliam Breathitt Gray 
590f1d8a071SWilliam Breathitt Gray 	*synchronous_mode = priv->synchronous_mode[channel_id];
591f1d8a071SWilliam Breathitt Gray 
592f1d8a071SWilliam Breathitt Gray 	return 0;
593f1d8a071SWilliam Breathitt Gray }
594f1d8a071SWilliam Breathitt Gray 
595f1d8a071SWilliam Breathitt Gray static int quad8_synchronous_mode_set(struct counter_device *counter,
596aaec1a0fSWilliam Breathitt Gray 				      struct counter_signal *signal,
597aaec1a0fSWilliam Breathitt Gray 				      u32 synchronous_mode)
598f1d8a071SWilliam Breathitt Gray {
599aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
600f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
601daae1ee5SWilliam Breathitt Gray 	u8 __iomem *const control = &priv->reg->channel[channel_id].control;
60209db4678SWilliam Breathitt Gray 	unsigned long irqflags;
603fc069262SSyed Nayyar Waris 	unsigned int idr_cfg = synchronous_mode;
604fc069262SSyed Nayyar Waris 
60509db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
606fc069262SSyed Nayyar Waris 
607fc069262SSyed Nayyar Waris 	idr_cfg |= priv->index_polarity[channel_id] << 1;
608f1d8a071SWilliam Breathitt Gray 
609f1d8a071SWilliam Breathitt Gray 	/* Index function must be non-synchronous in non-quadrature mode */
610fc069262SSyed Nayyar Waris 	if (synchronous_mode && !priv->quadrature_mode[channel_id]) {
61109db4678SWilliam Breathitt Gray 		spin_unlock_irqrestore(&priv->lock, irqflags);
612f1d8a071SWilliam Breathitt Gray 		return -EINVAL;
613fc069262SSyed Nayyar Waris 	}
614f1d8a071SWilliam Breathitt Gray 
615f1d8a071SWilliam Breathitt Gray 	priv->synchronous_mode[channel_id] = synchronous_mode;
616f1d8a071SWilliam Breathitt Gray 
617f1d8a071SWilliam Breathitt Gray 	/* Load Index Control configuration to Index Control Register */
618daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_IDR | idr_cfg, control);
619f1d8a071SWilliam Breathitt Gray 
62009db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
621fc069262SSyed Nayyar Waris 
622f1d8a071SWilliam Breathitt Gray 	return 0;
623f1d8a071SWilliam Breathitt Gray }
624f1d8a071SWilliam Breathitt Gray 
625aaec1a0fSWilliam Breathitt Gray static int quad8_count_floor_read(struct counter_device *counter,
626aaec1a0fSWilliam Breathitt Gray 				  struct counter_count *count, u64 *floor)
627f1d8a071SWilliam Breathitt Gray {
628f1d8a071SWilliam Breathitt Gray 	/* Only a floor of 0 is supported */
629aaec1a0fSWilliam Breathitt Gray 	*floor = 0;
630aaec1a0fSWilliam Breathitt Gray 
631aaec1a0fSWilliam Breathitt Gray 	return 0;
632f1d8a071SWilliam Breathitt Gray }
633f1d8a071SWilliam Breathitt Gray 
634aaec1a0fSWilliam Breathitt Gray static int quad8_count_mode_read(struct counter_device *counter,
635aaec1a0fSWilliam Breathitt Gray 				 struct counter_count *count,
636aaec1a0fSWilliam Breathitt Gray 				 enum counter_count_mode *cnt_mode)
637f1d8a071SWilliam Breathitt Gray {
638aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
639f1d8a071SWilliam Breathitt Gray 
640f1d8a071SWilliam Breathitt Gray 	/* Map 104-QUAD-8 count mode to Generic Counter count mode */
641f1d8a071SWilliam Breathitt Gray 	switch (priv->count_mode[count->id]) {
642f1d8a071SWilliam Breathitt Gray 	case 0:
643f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_NORMAL;
644f1d8a071SWilliam Breathitt Gray 		break;
645f1d8a071SWilliam Breathitt Gray 	case 1:
646f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_RANGE_LIMIT;
647f1d8a071SWilliam Breathitt Gray 		break;
648f1d8a071SWilliam Breathitt Gray 	case 2:
649f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_NON_RECYCLE;
650f1d8a071SWilliam Breathitt Gray 		break;
651f1d8a071SWilliam Breathitt Gray 	case 3:
652f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_MODULO_N;
653f1d8a071SWilliam Breathitt Gray 		break;
654f1d8a071SWilliam Breathitt Gray 	}
655f1d8a071SWilliam Breathitt Gray 
656f1d8a071SWilliam Breathitt Gray 	return 0;
657f1d8a071SWilliam Breathitt Gray }
658f1d8a071SWilliam Breathitt Gray 
659aaec1a0fSWilliam Breathitt Gray static int quad8_count_mode_write(struct counter_device *counter,
660aaec1a0fSWilliam Breathitt Gray 				  struct counter_count *count,
661aaec1a0fSWilliam Breathitt Gray 				  enum counter_count_mode cnt_mode)
662f1d8a071SWilliam Breathitt Gray {
663aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
664aaec1a0fSWilliam Breathitt Gray 	unsigned int count_mode;
665f1d8a071SWilliam Breathitt Gray 	unsigned int mode_cfg;
666daae1ee5SWilliam Breathitt Gray 	u8 __iomem *const control = &priv->reg->channel[count->id].control;
66709db4678SWilliam Breathitt Gray 	unsigned long irqflags;
668f1d8a071SWilliam Breathitt Gray 
669f1d8a071SWilliam Breathitt Gray 	/* Map Generic Counter count mode to 104-QUAD-8 count mode */
670f1d8a071SWilliam Breathitt Gray 	switch (cnt_mode) {
671f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_NORMAL:
672aaec1a0fSWilliam Breathitt Gray 		count_mode = 0;
673f1d8a071SWilliam Breathitt Gray 		break;
674f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_RANGE_LIMIT:
675aaec1a0fSWilliam Breathitt Gray 		count_mode = 1;
676f1d8a071SWilliam Breathitt Gray 		break;
677f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_NON_RECYCLE:
678aaec1a0fSWilliam Breathitt Gray 		count_mode = 2;
679f1d8a071SWilliam Breathitt Gray 		break;
680f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_MODULO_N:
681aaec1a0fSWilliam Breathitt Gray 		count_mode = 3;
682f1d8a071SWilliam Breathitt Gray 		break;
683b11eed15SWilliam Breathitt Gray 	default:
684b11eed15SWilliam Breathitt Gray 		/* should never reach this path */
685b11eed15SWilliam Breathitt Gray 		return -EINVAL;
686f1d8a071SWilliam Breathitt Gray 	}
687f1d8a071SWilliam Breathitt Gray 
68809db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
689fc069262SSyed Nayyar Waris 
690aaec1a0fSWilliam Breathitt Gray 	priv->count_mode[count->id] = count_mode;
691f1d8a071SWilliam Breathitt Gray 
692f1d8a071SWilliam Breathitt Gray 	/* Set count mode configuration value */
693aaec1a0fSWilliam Breathitt Gray 	mode_cfg = count_mode << 1;
694f1d8a071SWilliam Breathitt Gray 
695f1d8a071SWilliam Breathitt Gray 	/* Add quadrature mode configuration */
696f1d8a071SWilliam Breathitt Gray 	if (priv->quadrature_mode[count->id])
697f1d8a071SWilliam Breathitt Gray 		mode_cfg |= (priv->quadrature_scale[count->id] + 1) << 3;
698f1d8a071SWilliam Breathitt Gray 
699f1d8a071SWilliam Breathitt Gray 	/* Load mode configuration to Counter Mode Register */
700daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_CMR | mode_cfg, control);
701f1d8a071SWilliam Breathitt Gray 
70209db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
703fc069262SSyed Nayyar Waris 
704f1d8a071SWilliam Breathitt Gray 	return 0;
705f1d8a071SWilliam Breathitt Gray }
706f1d8a071SWilliam Breathitt Gray 
707aaec1a0fSWilliam Breathitt Gray static int quad8_count_enable_read(struct counter_device *counter,
708aaec1a0fSWilliam Breathitt Gray 				   struct counter_count *count, u8 *enable)
709f1d8a071SWilliam Breathitt Gray {
710aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
711f1d8a071SWilliam Breathitt Gray 
712aaec1a0fSWilliam Breathitt Gray 	*enable = priv->ab_enable[count->id];
713aaec1a0fSWilliam Breathitt Gray 
714aaec1a0fSWilliam Breathitt Gray 	return 0;
715f1d8a071SWilliam Breathitt Gray }
716f1d8a071SWilliam Breathitt Gray 
717aaec1a0fSWilliam Breathitt Gray static int quad8_count_enable_write(struct counter_device *counter,
718aaec1a0fSWilliam Breathitt Gray 				    struct counter_count *count, u8 enable)
719f1d8a071SWilliam Breathitt Gray {
720aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
721daae1ee5SWilliam Breathitt Gray 	u8 __iomem *const control = &priv->reg->channel[count->id].control;
72209db4678SWilliam Breathitt Gray 	unsigned long irqflags;
723f1d8a071SWilliam Breathitt Gray 	unsigned int ior_cfg;
724f1d8a071SWilliam Breathitt Gray 
72509db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
726fc069262SSyed Nayyar Waris 
727aaec1a0fSWilliam Breathitt Gray 	priv->ab_enable[count->id] = enable;
728f1d8a071SWilliam Breathitt Gray 
7297aa2ba0dSWilliam Breathitt Gray 	ior_cfg = enable | priv->preset_enable[count->id] << 1 |
7307aa2ba0dSWilliam Breathitt Gray 		  priv->irq_trigger[count->id] << 3;
731f1d8a071SWilliam Breathitt Gray 
732f1d8a071SWilliam Breathitt Gray 	/* Load I/O control configuration */
733daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_IOR | ior_cfg, control);
734f1d8a071SWilliam Breathitt Gray 
73509db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
736fc069262SSyed Nayyar Waris 
737aaec1a0fSWilliam Breathitt Gray 	return 0;
738f1d8a071SWilliam Breathitt Gray }
739f1d8a071SWilliam Breathitt Gray 
740e357e81fSWilliam Breathitt Gray static const char *const quad8_noise_error_states[] = {
741e357e81fSWilliam Breathitt Gray 	"No excessive noise is present at the count inputs",
742e357e81fSWilliam Breathitt Gray 	"Excessive noise is present at the count inputs"
743e357e81fSWilliam Breathitt Gray };
744e357e81fSWilliam Breathitt Gray 
745f1d8a071SWilliam Breathitt Gray static int quad8_error_noise_get(struct counter_device *counter,
746aaec1a0fSWilliam Breathitt Gray 				 struct counter_count *count, u32 *noise_error)
747f1d8a071SWilliam Breathitt Gray {
748aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
749daae1ee5SWilliam Breathitt Gray 	u8 __iomem *const flag_addr = &priv->reg->channel[count->id].control;
750f1d8a071SWilliam Breathitt Gray 
751daae1ee5SWilliam Breathitt Gray 	*noise_error = !!(ioread8(flag_addr) & QUAD8_FLAG_E);
752f1d8a071SWilliam Breathitt Gray 
753f1d8a071SWilliam Breathitt Gray 	return 0;
754f1d8a071SWilliam Breathitt Gray }
755f1d8a071SWilliam Breathitt Gray 
756aaec1a0fSWilliam Breathitt Gray static int quad8_count_preset_read(struct counter_device *counter,
757aaec1a0fSWilliam Breathitt Gray 				   struct counter_count *count, u64 *preset)
758f1d8a071SWilliam Breathitt Gray {
759aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
760f1d8a071SWilliam Breathitt Gray 
761aaec1a0fSWilliam Breathitt Gray 	*preset = priv->preset[count->id];
762aaec1a0fSWilliam Breathitt Gray 
763aaec1a0fSWilliam Breathitt Gray 	return 0;
764f1d8a071SWilliam Breathitt Gray }
765f1d8a071SWilliam Breathitt Gray 
766e612b600SWilliam Breathitt Gray static void quad8_preset_register_set(struct quad8 *const priv, const int id,
767e612b600SWilliam Breathitt Gray 				      const unsigned int preset)
768fc069262SSyed Nayyar Waris {
769daae1ee5SWilliam Breathitt Gray 	struct channel_reg __iomem *const chan = priv->reg->channel + id;
770fc069262SSyed Nayyar Waris 	int i;
771fc069262SSyed Nayyar Waris 
772e357e81fSWilliam Breathitt Gray 	priv->preset[id] = preset;
773fc069262SSyed Nayyar Waris 
774fc069262SSyed Nayyar Waris 	/* Reset Byte Pointer */
775daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, &chan->control);
776fc069262SSyed Nayyar Waris 
777fc069262SSyed Nayyar Waris 	/* Set Preset Register */
778fc069262SSyed Nayyar Waris 	for (i = 0; i < 3; i++)
779daae1ee5SWilliam Breathitt Gray 		iowrite8(preset >> (8 * i), &chan->data);
780fc069262SSyed Nayyar Waris }
781fc069262SSyed Nayyar Waris 
782aaec1a0fSWilliam Breathitt Gray static int quad8_count_preset_write(struct counter_device *counter,
783aaec1a0fSWilliam Breathitt Gray 				    struct counter_count *count, u64 preset)
784f1d8a071SWilliam Breathitt Gray {
785aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
78609db4678SWilliam Breathitt Gray 	unsigned long irqflags;
787f1d8a071SWilliam Breathitt Gray 
788f1d8a071SWilliam Breathitt Gray 	/* Only 24-bit values are supported */
789f1d8a071SWilliam Breathitt Gray 	if (preset > 0xFFFFFF)
790e2ff3198SWilliam Breathitt Gray 		return -ERANGE;
791f1d8a071SWilliam Breathitt Gray 
79209db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
793f1d8a071SWilliam Breathitt Gray 
794fc069262SSyed Nayyar Waris 	quad8_preset_register_set(priv, count->id, preset);
795f1d8a071SWilliam Breathitt Gray 
79609db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
797f1d8a071SWilliam Breathitt Gray 
798aaec1a0fSWilliam Breathitt Gray 	return 0;
799f1d8a071SWilliam Breathitt Gray }
800f1d8a071SWilliam Breathitt Gray 
801aaec1a0fSWilliam Breathitt Gray static int quad8_count_ceiling_read(struct counter_device *counter,
802aaec1a0fSWilliam Breathitt Gray 				    struct counter_count *count, u64 *ceiling)
803f1d8a071SWilliam Breathitt Gray {
804aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
80509db4678SWilliam Breathitt Gray 	unsigned long irqflags;
806fc069262SSyed Nayyar Waris 
80709db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
808f1d8a071SWilliam Breathitt Gray 
809f1d8a071SWilliam Breathitt Gray 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
810f1d8a071SWilliam Breathitt Gray 	switch (priv->count_mode[count->id]) {
811f1d8a071SWilliam Breathitt Gray 	case 1:
812f1d8a071SWilliam Breathitt Gray 	case 3:
813aaec1a0fSWilliam Breathitt Gray 		*ceiling = priv->preset[count->id];
814aaec1a0fSWilliam Breathitt Gray 		break;
815aaec1a0fSWilliam Breathitt Gray 	default:
816f1d8a071SWilliam Breathitt Gray 		/* By default 0x1FFFFFF (25 bits unsigned) is maximum count */
817aaec1a0fSWilliam Breathitt Gray 		*ceiling = 0x1FFFFFF;
818aaec1a0fSWilliam Breathitt Gray 		break;
819f1d8a071SWilliam Breathitt Gray 	}
820f1d8a071SWilliam Breathitt Gray 
82109db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
822aaec1a0fSWilliam Breathitt Gray 
823aaec1a0fSWilliam Breathitt Gray 	return 0;
824aaec1a0fSWilliam Breathitt Gray }
825aaec1a0fSWilliam Breathitt Gray 
826aaec1a0fSWilliam Breathitt Gray static int quad8_count_ceiling_write(struct counter_device *counter,
827aaec1a0fSWilliam Breathitt Gray 				     struct counter_count *count, u64 ceiling)
828f1d8a071SWilliam Breathitt Gray {
829aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
83009db4678SWilliam Breathitt Gray 	unsigned long irqflags;
831fc069262SSyed Nayyar Waris 
832fc069262SSyed Nayyar Waris 	/* Only 24-bit values are supported */
833fc069262SSyed Nayyar Waris 	if (ceiling > 0xFFFFFF)
834e2ff3198SWilliam Breathitt Gray 		return -ERANGE;
835fc069262SSyed Nayyar Waris 
83609db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
837f1d8a071SWilliam Breathitt Gray 
838f1d8a071SWilliam Breathitt Gray 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
839f1d8a071SWilliam Breathitt Gray 	switch (priv->count_mode[count->id]) {
840f1d8a071SWilliam Breathitt Gray 	case 1:
841f1d8a071SWilliam Breathitt Gray 	case 3:
842fc069262SSyed Nayyar Waris 		quad8_preset_register_set(priv, count->id, ceiling);
84309db4678SWilliam Breathitt Gray 		spin_unlock_irqrestore(&priv->lock, irqflags);
844aaec1a0fSWilliam Breathitt Gray 		return 0;
845f1d8a071SWilliam Breathitt Gray 	}
846f1d8a071SWilliam Breathitt Gray 
84709db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
848fc069262SSyed Nayyar Waris 
849728246e8SWilliam Breathitt Gray 	return -EINVAL;
850f1d8a071SWilliam Breathitt Gray }
851f1d8a071SWilliam Breathitt Gray 
852aaec1a0fSWilliam Breathitt Gray static int quad8_count_preset_enable_read(struct counter_device *counter,
853aaec1a0fSWilliam Breathitt Gray 					  struct counter_count *count,
854aaec1a0fSWilliam Breathitt Gray 					  u8 *preset_enable)
855f1d8a071SWilliam Breathitt Gray {
856aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
857f1d8a071SWilliam Breathitt Gray 
858aaec1a0fSWilliam Breathitt Gray 	*preset_enable = !priv->preset_enable[count->id];
859aaec1a0fSWilliam Breathitt Gray 
860aaec1a0fSWilliam Breathitt Gray 	return 0;
861f1d8a071SWilliam Breathitt Gray }
862f1d8a071SWilliam Breathitt Gray 
863aaec1a0fSWilliam Breathitt Gray static int quad8_count_preset_enable_write(struct counter_device *counter,
864aaec1a0fSWilliam Breathitt Gray 					   struct counter_count *count,
865aaec1a0fSWilliam Breathitt Gray 					   u8 preset_enable)
866f1d8a071SWilliam Breathitt Gray {
867aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
868daae1ee5SWilliam Breathitt Gray 	u8 __iomem *const control = &priv->reg->channel[count->id].control;
86909db4678SWilliam Breathitt Gray 	unsigned long irqflags;
870f1d8a071SWilliam Breathitt Gray 	unsigned int ior_cfg;
871f1d8a071SWilliam Breathitt Gray 
872f1d8a071SWilliam Breathitt Gray 	/* Preset enable is active low in Input/Output Control register */
873f1d8a071SWilliam Breathitt Gray 	preset_enable = !preset_enable;
874f1d8a071SWilliam Breathitt Gray 
87509db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
876fc069262SSyed Nayyar Waris 
877f1d8a071SWilliam Breathitt Gray 	priv->preset_enable[count->id] = preset_enable;
878f1d8a071SWilliam Breathitt Gray 
8797aa2ba0dSWilliam Breathitt Gray 	ior_cfg = priv->ab_enable[count->id] | preset_enable << 1 |
8807aa2ba0dSWilliam Breathitt Gray 		  priv->irq_trigger[count->id] << 3;
881f1d8a071SWilliam Breathitt Gray 
882f1d8a071SWilliam Breathitt Gray 	/* Load I/O control configuration to Input / Output Control Register */
883daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_IOR | ior_cfg, control);
884f1d8a071SWilliam Breathitt Gray 
88509db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
886fc069262SSyed Nayyar Waris 
887aaec1a0fSWilliam Breathitt Gray 	return 0;
888f1d8a071SWilliam Breathitt Gray }
889f1d8a071SWilliam Breathitt Gray 
890aaec1a0fSWilliam Breathitt Gray static int quad8_signal_cable_fault_read(struct counter_device *counter,
891954ab5ccSWilliam Breathitt Gray 					 struct counter_signal *signal,
892aaec1a0fSWilliam Breathitt Gray 					 u8 *cable_fault)
893954ab5ccSWilliam Breathitt Gray {
894aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
895954ab5ccSWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
89609db4678SWilliam Breathitt Gray 	unsigned long irqflags;
897708d9893SSyed Nayyar Waris 	bool disabled;
898954ab5ccSWilliam Breathitt Gray 	unsigned int status;
899954ab5ccSWilliam Breathitt Gray 
90009db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
901708d9893SSyed Nayyar Waris 
902708d9893SSyed Nayyar Waris 	disabled = !(priv->cable_fault_enable & BIT(channel_id));
903708d9893SSyed Nayyar Waris 
904708d9893SSyed Nayyar Waris 	if (disabled) {
90509db4678SWilliam Breathitt Gray 		spin_unlock_irqrestore(&priv->lock, irqflags);
906954ab5ccSWilliam Breathitt Gray 		return -EINVAL;
907708d9893SSyed Nayyar Waris 	}
908954ab5ccSWilliam Breathitt Gray 
909954ab5ccSWilliam Breathitt Gray 	/* Logic 0 = cable fault */
910daae1ee5SWilliam Breathitt Gray 	status = ioread8(&priv->reg->cable_status);
911954ab5ccSWilliam Breathitt Gray 
91209db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
913708d9893SSyed Nayyar Waris 
914954ab5ccSWilliam Breathitt Gray 	/* Mask respective channel and invert logic */
915aaec1a0fSWilliam Breathitt Gray 	*cable_fault = !(status & BIT(channel_id));
916954ab5ccSWilliam Breathitt Gray 
917aaec1a0fSWilliam Breathitt Gray 	return 0;
918954ab5ccSWilliam Breathitt Gray }
919954ab5ccSWilliam Breathitt Gray 
920aaec1a0fSWilliam Breathitt Gray static int quad8_signal_cable_fault_enable_read(struct counter_device *counter,
921aaec1a0fSWilliam Breathitt Gray 						struct counter_signal *signal,
922aaec1a0fSWilliam Breathitt Gray 						u8 *enable)
923954ab5ccSWilliam Breathitt Gray {
924aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
925954ab5ccSWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
926954ab5ccSWilliam Breathitt Gray 
927aaec1a0fSWilliam Breathitt Gray 	*enable = !!(priv->cable_fault_enable & BIT(channel_id));
928aaec1a0fSWilliam Breathitt Gray 
929aaec1a0fSWilliam Breathitt Gray 	return 0;
930954ab5ccSWilliam Breathitt Gray }
931954ab5ccSWilliam Breathitt Gray 
932aaec1a0fSWilliam Breathitt Gray static int quad8_signal_cable_fault_enable_write(struct counter_device *counter,
933aaec1a0fSWilliam Breathitt Gray 						 struct counter_signal *signal,
934aaec1a0fSWilliam Breathitt Gray 						 u8 enable)
935954ab5ccSWilliam Breathitt Gray {
936aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
937954ab5ccSWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
93809db4678SWilliam Breathitt Gray 	unsigned long irqflags;
939954ab5ccSWilliam Breathitt Gray 	unsigned int cable_fault_enable;
940954ab5ccSWilliam Breathitt Gray 
94109db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
942708d9893SSyed Nayyar Waris 
943954ab5ccSWilliam Breathitt Gray 	if (enable)
944954ab5ccSWilliam Breathitt Gray 		priv->cable_fault_enable |= BIT(channel_id);
945954ab5ccSWilliam Breathitt Gray 	else
946954ab5ccSWilliam Breathitt Gray 		priv->cable_fault_enable &= ~BIT(channel_id);
947954ab5ccSWilliam Breathitt Gray 
948954ab5ccSWilliam Breathitt Gray 	/* Enable is active low in Differential Encoder Cable Status register */
949954ab5ccSWilliam Breathitt Gray 	cable_fault_enable = ~priv->cable_fault_enable;
950954ab5ccSWilliam Breathitt Gray 
951daae1ee5SWilliam Breathitt Gray 	iowrite8(cable_fault_enable, &priv->reg->cable_status);
952954ab5ccSWilliam Breathitt Gray 
95309db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
954708d9893SSyed Nayyar Waris 
955aaec1a0fSWilliam Breathitt Gray 	return 0;
956954ab5ccSWilliam Breathitt Gray }
957954ab5ccSWilliam Breathitt Gray 
958aaec1a0fSWilliam Breathitt Gray static int quad8_signal_fck_prescaler_read(struct counter_device *counter,
959aaec1a0fSWilliam Breathitt Gray 					   struct counter_signal *signal,
960aaec1a0fSWilliam Breathitt Gray 					   u8 *prescaler)
961de65d055SWilliam Breathitt Gray {
962aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
963de65d055SWilliam Breathitt Gray 
964aaec1a0fSWilliam Breathitt Gray 	*prescaler = priv->fck_prescaler[signal->id / 2];
965aaec1a0fSWilliam Breathitt Gray 
966aaec1a0fSWilliam Breathitt Gray 	return 0;
967de65d055SWilliam Breathitt Gray }
968de65d055SWilliam Breathitt Gray 
969aaec1a0fSWilliam Breathitt Gray static int quad8_signal_fck_prescaler_write(struct counter_device *counter,
970aaec1a0fSWilliam Breathitt Gray 					    struct counter_signal *signal,
971aaec1a0fSWilliam Breathitt Gray 					    u8 prescaler)
972de65d055SWilliam Breathitt Gray {
973aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
974de65d055SWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
975daae1ee5SWilliam Breathitt Gray 	struct channel_reg __iomem *const chan = priv->reg->channel + channel_id;
97609db4678SWilliam Breathitt Gray 	unsigned long irqflags;
977de65d055SWilliam Breathitt Gray 
97809db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
979d5ed76adSSyed Nayyar Waris 
980de65d055SWilliam Breathitt Gray 	priv->fck_prescaler[channel_id] = prescaler;
981de65d055SWilliam Breathitt Gray 
982de65d055SWilliam Breathitt Gray 	/* Reset Byte Pointer */
983daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, &chan->control);
984de65d055SWilliam Breathitt Gray 
985de65d055SWilliam Breathitt Gray 	/* Set filter clock factor */
986daae1ee5SWilliam Breathitt Gray 	iowrite8(prescaler, &chan->data);
987b6e9cdedSWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
988daae1ee5SWilliam Breathitt Gray 		 &chan->control);
989de65d055SWilliam Breathitt Gray 
99009db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
991d5ed76adSSyed Nayyar Waris 
992aaec1a0fSWilliam Breathitt Gray 	return 0;
993de65d055SWilliam Breathitt Gray }
994de65d055SWilliam Breathitt Gray 
995aaec1a0fSWilliam Breathitt Gray static struct counter_comp quad8_signal_ext[] = {
996aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_BOOL("cable_fault", quad8_signal_cable_fault_read,
997aaec1a0fSWilliam Breathitt Gray 				 NULL),
998aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_BOOL("cable_fault_enable",
999aaec1a0fSWilliam Breathitt Gray 				 quad8_signal_cable_fault_enable_read,
1000aaec1a0fSWilliam Breathitt Gray 				 quad8_signal_cable_fault_enable_write),
1001aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_U8("filter_clock_prescaler",
1002aaec1a0fSWilliam Breathitt Gray 			       quad8_signal_fck_prescaler_read,
1003aaec1a0fSWilliam Breathitt Gray 			       quad8_signal_fck_prescaler_write)
1004de65d055SWilliam Breathitt Gray };
1005de65d055SWilliam Breathitt Gray 
1006*9830288aSWilliam Breathitt Gray static const enum counter_signal_polarity quad8_polarities[] = {
1007*9830288aSWilliam Breathitt Gray 	COUNTER_SIGNAL_POLARITY_POSITIVE,
1008*9830288aSWilliam Breathitt Gray 	COUNTER_SIGNAL_POLARITY_NEGATIVE,
1009*9830288aSWilliam Breathitt Gray };
1010*9830288aSWilliam Breathitt Gray 
1011*9830288aSWilliam Breathitt Gray static DEFINE_COUNTER_AVAILABLE(quad8_polarity_available, quad8_polarities);
1012*9830288aSWilliam Breathitt Gray 
1013aaec1a0fSWilliam Breathitt Gray static DEFINE_COUNTER_ENUM(quad8_index_pol_enum, quad8_index_polarity_modes);
1014aaec1a0fSWilliam Breathitt Gray static DEFINE_COUNTER_ENUM(quad8_synch_mode_enum, quad8_synchronous_modes);
1015aaec1a0fSWilliam Breathitt Gray 
1016aaec1a0fSWilliam Breathitt Gray static struct counter_comp quad8_index_ext[] = {
1017aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_ENUM("index_polarity", quad8_index_polarity_get,
1018aaec1a0fSWilliam Breathitt Gray 				 quad8_index_polarity_set,
1019aaec1a0fSWilliam Breathitt Gray 				 quad8_index_pol_enum),
1020*9830288aSWilliam Breathitt Gray 	COUNTER_COMP_POLARITY(quad8_polarity_read, quad8_polarity_write,
1021*9830288aSWilliam Breathitt Gray 			      quad8_polarity_available),
1022aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_ENUM("synchronous_mode", quad8_synchronous_mode_get,
1023aaec1a0fSWilliam Breathitt Gray 				 quad8_synchronous_mode_set,
1024aaec1a0fSWilliam Breathitt Gray 				 quad8_synch_mode_enum),
1025f1d8a071SWilliam Breathitt Gray };
1026f1d8a071SWilliam Breathitt Gray 
1027f1d8a071SWilliam Breathitt Gray #define QUAD8_QUAD_SIGNAL(_id, _name) {		\
1028f1d8a071SWilliam Breathitt Gray 	.id = (_id),				\
1029de65d055SWilliam Breathitt Gray 	.name = (_name),			\
1030de65d055SWilliam Breathitt Gray 	.ext = quad8_signal_ext,		\
1031de65d055SWilliam Breathitt Gray 	.num_ext = ARRAY_SIZE(quad8_signal_ext)	\
1032f1d8a071SWilliam Breathitt Gray }
1033f1d8a071SWilliam Breathitt Gray 
1034f1d8a071SWilliam Breathitt Gray #define	QUAD8_INDEX_SIGNAL(_id, _name) {	\
1035f1d8a071SWilliam Breathitt Gray 	.id = (_id),				\
1036f1d8a071SWilliam Breathitt Gray 	.name = (_name),			\
1037f1d8a071SWilliam Breathitt Gray 	.ext = quad8_index_ext,			\
1038f1d8a071SWilliam Breathitt Gray 	.num_ext = ARRAY_SIZE(quad8_index_ext)	\
1039f1d8a071SWilliam Breathitt Gray }
1040f1d8a071SWilliam Breathitt Gray 
1041f1d8a071SWilliam Breathitt Gray static struct counter_signal quad8_signals[] = {
1042f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(0, "Channel 1 Quadrature A"),
1043f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(1, "Channel 1 Quadrature B"),
1044f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(2, "Channel 2 Quadrature A"),
1045f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(3, "Channel 2 Quadrature B"),
1046f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(4, "Channel 3 Quadrature A"),
1047f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(5, "Channel 3 Quadrature B"),
1048f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(6, "Channel 4 Quadrature A"),
1049f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(7, "Channel 4 Quadrature B"),
1050f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(8, "Channel 5 Quadrature A"),
1051f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(9, "Channel 5 Quadrature B"),
1052f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(10, "Channel 6 Quadrature A"),
1053f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(11, "Channel 6 Quadrature B"),
1054f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(12, "Channel 7 Quadrature A"),
1055f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(13, "Channel 7 Quadrature B"),
1056f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(14, "Channel 8 Quadrature A"),
1057f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(15, "Channel 8 Quadrature B"),
1058f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(16, "Channel 1 Index"),
1059f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(17, "Channel 2 Index"),
1060f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(18, "Channel 3 Index"),
1061f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(19, "Channel 4 Index"),
1062f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(20, "Channel 5 Index"),
1063f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(21, "Channel 6 Index"),
1064f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(22, "Channel 7 Index"),
1065f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(23, "Channel 8 Index")
1066f1d8a071SWilliam Breathitt Gray };
1067f1d8a071SWilliam Breathitt Gray 
1068f1d8a071SWilliam Breathitt Gray #define QUAD8_COUNT_SYNAPSES(_id) {					\
1069f1d8a071SWilliam Breathitt Gray 	{								\
1070f1d8a071SWilliam Breathitt Gray 		.actions_list = quad8_synapse_actions_list,		\
1071f1d8a071SWilliam Breathitt Gray 		.num_actions = ARRAY_SIZE(quad8_synapse_actions_list),	\
1072f1d8a071SWilliam Breathitt Gray 		.signal = quad8_signals + 2 * (_id)			\
1073f1d8a071SWilliam Breathitt Gray 	},								\
1074f1d8a071SWilliam Breathitt Gray 	{								\
1075f1d8a071SWilliam Breathitt Gray 		.actions_list = quad8_synapse_actions_list,		\
1076f1d8a071SWilliam Breathitt Gray 		.num_actions = ARRAY_SIZE(quad8_synapse_actions_list),	\
1077f1d8a071SWilliam Breathitt Gray 		.signal = quad8_signals + 2 * (_id) + 1			\
1078f1d8a071SWilliam Breathitt Gray 	},								\
1079f1d8a071SWilliam Breathitt Gray 	{								\
1080f1d8a071SWilliam Breathitt Gray 		.actions_list = quad8_index_actions_list,		\
1081f1d8a071SWilliam Breathitt Gray 		.num_actions = ARRAY_SIZE(quad8_index_actions_list),	\
1082f1d8a071SWilliam Breathitt Gray 		.signal = quad8_signals + 2 * (_id) + 16		\
1083f1d8a071SWilliam Breathitt Gray 	}								\
1084f1d8a071SWilliam Breathitt Gray }
1085f1d8a071SWilliam Breathitt Gray 
1086f1d8a071SWilliam Breathitt Gray static struct counter_synapse quad8_count_synapses[][3] = {
1087f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(0), QUAD8_COUNT_SYNAPSES(1),
1088f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(2), QUAD8_COUNT_SYNAPSES(3),
1089f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(4), QUAD8_COUNT_SYNAPSES(5),
1090f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(6), QUAD8_COUNT_SYNAPSES(7)
1091f1d8a071SWilliam Breathitt Gray };
1092f1d8a071SWilliam Breathitt Gray 
1093aaec1a0fSWilliam Breathitt Gray static const enum counter_count_mode quad8_cnt_modes[] = {
1094aaec1a0fSWilliam Breathitt Gray 	COUNTER_COUNT_MODE_NORMAL,
1095aaec1a0fSWilliam Breathitt Gray 	COUNTER_COUNT_MODE_RANGE_LIMIT,
1096aaec1a0fSWilliam Breathitt Gray 	COUNTER_COUNT_MODE_NON_RECYCLE,
1097aaec1a0fSWilliam Breathitt Gray 	COUNTER_COUNT_MODE_MODULO_N,
1098aaec1a0fSWilliam Breathitt Gray };
1099aaec1a0fSWilliam Breathitt Gray 
1100aaec1a0fSWilliam Breathitt Gray static DEFINE_COUNTER_AVAILABLE(quad8_count_mode_available, quad8_cnt_modes);
1101aaec1a0fSWilliam Breathitt Gray 
1102aaec1a0fSWilliam Breathitt Gray static DEFINE_COUNTER_ENUM(quad8_error_noise_enum, quad8_noise_error_states);
1103aaec1a0fSWilliam Breathitt Gray 
1104aaec1a0fSWilliam Breathitt Gray static struct counter_comp quad8_count_ext[] = {
1105aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_CEILING(quad8_count_ceiling_read,
1106aaec1a0fSWilliam Breathitt Gray 			     quad8_count_ceiling_write),
1107aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_FLOOR(quad8_count_floor_read, NULL),
1108aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_COUNT_MODE(quad8_count_mode_read, quad8_count_mode_write,
1109aaec1a0fSWilliam Breathitt Gray 				quad8_count_mode_available),
1110aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_DIRECTION(quad8_direction_read),
1111aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_ENABLE(quad8_count_enable_read, quad8_count_enable_write),
1112aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_COUNT_ENUM("error_noise", quad8_error_noise_get, NULL,
1113aaec1a0fSWilliam Breathitt Gray 				quad8_error_noise_enum),
1114aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_PRESET(quad8_count_preset_read, quad8_count_preset_write),
1115aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_PRESET_ENABLE(quad8_count_preset_enable_read,
1116aaec1a0fSWilliam Breathitt Gray 				   quad8_count_preset_enable_write),
1117f1d8a071SWilliam Breathitt Gray };
1118f1d8a071SWilliam Breathitt Gray 
1119f1d8a071SWilliam Breathitt Gray #define QUAD8_COUNT(_id, _cntname) {					\
1120f1d8a071SWilliam Breathitt Gray 	.id = (_id),							\
1121f1d8a071SWilliam Breathitt Gray 	.name = (_cntname),						\
1122f1d8a071SWilliam Breathitt Gray 	.functions_list = quad8_count_functions_list,			\
1123f1d8a071SWilliam Breathitt Gray 	.num_functions = ARRAY_SIZE(quad8_count_functions_list),	\
1124f1d8a071SWilliam Breathitt Gray 	.synapses = quad8_count_synapses[(_id)],			\
1125f1d8a071SWilliam Breathitt Gray 	.num_synapses =	2,						\
1126f1d8a071SWilliam Breathitt Gray 	.ext = quad8_count_ext,						\
1127f1d8a071SWilliam Breathitt Gray 	.num_ext = ARRAY_SIZE(quad8_count_ext)				\
1128f1d8a071SWilliam Breathitt Gray }
1129f1d8a071SWilliam Breathitt Gray 
1130f1d8a071SWilliam Breathitt Gray static struct counter_count quad8_counts[] = {
1131f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(0, "Channel 1 Count"),
1132f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(1, "Channel 2 Count"),
1133f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(2, "Channel 3 Count"),
1134f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(3, "Channel 4 Count"),
1135f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(4, "Channel 5 Count"),
1136f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(5, "Channel 6 Count"),
1137f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(6, "Channel 7 Count"),
1138f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(7, "Channel 8 Count")
1139f1d8a071SWilliam Breathitt Gray };
1140f1d8a071SWilliam Breathitt Gray 
11417aa2ba0dSWilliam Breathitt Gray static irqreturn_t quad8_irq_handler(int irq, void *private)
11427aa2ba0dSWilliam Breathitt Gray {
11439e884bb1SUwe Kleine-König 	struct counter_device *counter = private;
11449e884bb1SUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
11457aa2ba0dSWilliam Breathitt Gray 	unsigned long irq_status;
11467aa2ba0dSWilliam Breathitt Gray 	unsigned long channel;
11477aa2ba0dSWilliam Breathitt Gray 	u8 event;
11487aa2ba0dSWilliam Breathitt Gray 
1149daae1ee5SWilliam Breathitt Gray 	irq_status = ioread8(&priv->reg->interrupt_status);
11507aa2ba0dSWilliam Breathitt Gray 	if (!irq_status)
11517aa2ba0dSWilliam Breathitt Gray 		return IRQ_NONE;
11527aa2ba0dSWilliam Breathitt Gray 
11537aa2ba0dSWilliam Breathitt Gray 	for_each_set_bit(channel, &irq_status, QUAD8_NUM_COUNTERS) {
11547aa2ba0dSWilliam Breathitt Gray 		switch (priv->irq_trigger[channel]) {
11557aa2ba0dSWilliam Breathitt Gray 		case QUAD8_EVENT_CARRY:
11567aa2ba0dSWilliam Breathitt Gray 			event = COUNTER_EVENT_OVERFLOW;
11577aa2ba0dSWilliam Breathitt Gray 				break;
11587aa2ba0dSWilliam Breathitt Gray 		case QUAD8_EVENT_COMPARE:
11597aa2ba0dSWilliam Breathitt Gray 			event = COUNTER_EVENT_THRESHOLD;
11607aa2ba0dSWilliam Breathitt Gray 				break;
11617aa2ba0dSWilliam Breathitt Gray 		case QUAD8_EVENT_CARRY_BORROW:
11627aa2ba0dSWilliam Breathitt Gray 			event = COUNTER_EVENT_OVERFLOW_UNDERFLOW;
11637aa2ba0dSWilliam Breathitt Gray 				break;
11647aa2ba0dSWilliam Breathitt Gray 		case QUAD8_EVENT_INDEX:
11657aa2ba0dSWilliam Breathitt Gray 			event = COUNTER_EVENT_INDEX;
11667aa2ba0dSWilliam Breathitt Gray 				break;
11677aa2ba0dSWilliam Breathitt Gray 		default:
11687aa2ba0dSWilliam Breathitt Gray 			/* should never reach this path */
11697aa2ba0dSWilliam Breathitt Gray 			WARN_ONCE(true, "invalid interrupt trigger function %u configured for channel %lu\n",
11707aa2ba0dSWilliam Breathitt Gray 				  priv->irq_trigger[channel], channel);
11717aa2ba0dSWilliam Breathitt Gray 			continue;
11727aa2ba0dSWilliam Breathitt Gray 		}
11737aa2ba0dSWilliam Breathitt Gray 
11749e884bb1SUwe Kleine-König 		counter_push_event(counter, event, channel);
11757aa2ba0dSWilliam Breathitt Gray 	}
11767aa2ba0dSWilliam Breathitt Gray 
11777aa2ba0dSWilliam Breathitt Gray 	/* Clear pending interrupts on device */
1178daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, &priv->reg->channel_oper);
11797aa2ba0dSWilliam Breathitt Gray 
11807aa2ba0dSWilliam Breathitt Gray 	return IRQ_HANDLED;
11817aa2ba0dSWilliam Breathitt Gray }
11827aa2ba0dSWilliam Breathitt Gray 
1183daae1ee5SWilliam Breathitt Gray static void quad8_init_counter(struct channel_reg __iomem *const chan)
1184b6e9cdedSWilliam Breathitt Gray {
1185b6e9cdedSWilliam Breathitt Gray 	unsigned long i;
1186b6e9cdedSWilliam Breathitt Gray 
1187b6e9cdedSWilliam Breathitt Gray 	/* Reset Byte Pointer */
1188daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, &chan->control);
1189b6e9cdedSWilliam Breathitt Gray 	/* Reset filter clock factor */
1190daae1ee5SWilliam Breathitt Gray 	iowrite8(0, &chan->data);
1191b6e9cdedSWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
1192daae1ee5SWilliam Breathitt Gray 		 &chan->control);
1193b6e9cdedSWilliam Breathitt Gray 	/* Reset Byte Pointer */
1194daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, &chan->control);
1195b6e9cdedSWilliam Breathitt Gray 	/* Reset Preset Register */
1196b6e9cdedSWilliam Breathitt Gray 	for (i = 0; i < 3; i++)
1197daae1ee5SWilliam Breathitt Gray 		iowrite8(0x00, &chan->data);
1198b6e9cdedSWilliam Breathitt Gray 	/* Reset Borrow, Carry, Compare, and Sign flags */
1199daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, &chan->control);
1200b6e9cdedSWilliam Breathitt Gray 	/* Reset Error flag */
1201daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, &chan->control);
1202b6e9cdedSWilliam Breathitt Gray 	/* Binary encoding; Normal count; non-quadrature mode */
1203daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_CMR, &chan->control);
1204b6e9cdedSWilliam Breathitt Gray 	/* Disable A and B inputs; preset on index; FLG1 as Carry */
1205daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_IOR, &chan->control);
1206b6e9cdedSWilliam Breathitt Gray 	/* Disable index function; negative index polarity */
1207daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_IDR, &chan->control);
1208b6e9cdedSWilliam Breathitt Gray }
1209b6e9cdedSWilliam Breathitt Gray 
1210f1d8a071SWilliam Breathitt Gray static int quad8_probe(struct device *dev, unsigned int id)
1211f1d8a071SWilliam Breathitt Gray {
12129e884bb1SUwe Kleine-König 	struct counter_device *counter;
1213e357e81fSWilliam Breathitt Gray 	struct quad8 *priv;
1214b6e9cdedSWilliam Breathitt Gray 	unsigned long i;
12157aa2ba0dSWilliam Breathitt Gray 	int err;
1216f1d8a071SWilliam Breathitt Gray 
1217f1d8a071SWilliam Breathitt Gray 	if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) {
1218f1d8a071SWilliam Breathitt Gray 		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
1219f1d8a071SWilliam Breathitt Gray 			base[id], base[id] + QUAD8_EXTENT);
1220f1d8a071SWilliam Breathitt Gray 		return -EBUSY;
1221f1d8a071SWilliam Breathitt Gray 	}
1222f1d8a071SWilliam Breathitt Gray 
12239e884bb1SUwe Kleine-König 	counter = devm_counter_alloc(dev, sizeof(*priv));
12249e884bb1SUwe Kleine-König 	if (!counter)
1225f1d8a071SWilliam Breathitt Gray 		return -ENOMEM;
12269e884bb1SUwe Kleine-König 	priv = counter_priv(counter);
1227f1d8a071SWilliam Breathitt Gray 
1228daae1ee5SWilliam Breathitt Gray 	priv->reg = devm_ioport_map(dev, base[id], QUAD8_EXTENT);
1229daae1ee5SWilliam Breathitt Gray 	if (!priv->reg)
1230b6e9cdedSWilliam Breathitt Gray 		return -ENOMEM;
1231b6e9cdedSWilliam Breathitt Gray 
1232f1d8a071SWilliam Breathitt Gray 	/* Initialize Counter device and driver data */
12339e884bb1SUwe Kleine-König 	counter->name = dev_name(dev);
12349e884bb1SUwe Kleine-König 	counter->parent = dev;
12359e884bb1SUwe Kleine-König 	counter->ops = &quad8_ops;
12369e884bb1SUwe Kleine-König 	counter->counts = quad8_counts;
12379e884bb1SUwe Kleine-König 	counter->num_counts = ARRAY_SIZE(quad8_counts);
12389e884bb1SUwe Kleine-König 	counter->signals = quad8_signals;
12399e884bb1SUwe Kleine-König 	counter->num_signals = ARRAY_SIZE(quad8_signals);
1240f1d8a071SWilliam Breathitt Gray 
124109db4678SWilliam Breathitt Gray 	spin_lock_init(&priv->lock);
1242fc069262SSyed Nayyar Waris 
12437aa2ba0dSWilliam Breathitt Gray 	/* Reset Index/Interrupt Register */
1244daae1ee5SWilliam Breathitt Gray 	iowrite8(0x00, &priv->reg->index_interrupt);
1245f1d8a071SWilliam Breathitt Gray 	/* Reset all counters and disable interrupt function */
1246daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CHAN_OP_RESET_COUNTERS, &priv->reg->channel_oper);
1247f1d8a071SWilliam Breathitt Gray 	/* Set initial configuration for all counters */
1248b6e9cdedSWilliam Breathitt Gray 	for (i = 0; i < QUAD8_NUM_COUNTERS; i++)
1249daae1ee5SWilliam Breathitt Gray 		quad8_init_counter(priv->reg->channel + i);
1250954ab5ccSWilliam Breathitt Gray 	/* Disable Differential Encoder Cable Status for all channels */
1251daae1ee5SWilliam Breathitt Gray 	iowrite8(0xFF, &priv->reg->cable_status);
12527aa2ba0dSWilliam Breathitt Gray 	/* Enable all counters and enable interrupt function */
1253daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, &priv->reg->channel_oper);
12547aa2ba0dSWilliam Breathitt Gray 
1255663d8fb0SWilliam Breathitt Gray 	err = devm_request_irq(&counter->dev, irq[id], quad8_irq_handler,
1256663d8fb0SWilliam Breathitt Gray 			       IRQF_SHARED, counter->name, counter);
12577aa2ba0dSWilliam Breathitt Gray 	if (err)
12587aa2ba0dSWilliam Breathitt Gray 		return err;
1259f1d8a071SWilliam Breathitt Gray 
12609e884bb1SUwe Kleine-König 	err = devm_counter_add(dev, counter);
12619e884bb1SUwe Kleine-König 	if (err < 0)
12629e884bb1SUwe Kleine-König 		return dev_err_probe(dev, err, "Failed to add counter\n");
12639e884bb1SUwe Kleine-König 
12649e884bb1SUwe Kleine-König 	return 0;
1265f1d8a071SWilliam Breathitt Gray }
1266f1d8a071SWilliam Breathitt Gray 
1267f1d8a071SWilliam Breathitt Gray static struct isa_driver quad8_driver = {
1268f1d8a071SWilliam Breathitt Gray 	.probe = quad8_probe,
1269f1d8a071SWilliam Breathitt Gray 	.driver = {
1270f1d8a071SWilliam Breathitt Gray 		.name = "104-quad-8"
1271f1d8a071SWilliam Breathitt Gray 	}
1272f1d8a071SWilliam Breathitt Gray };
1273f1d8a071SWilliam Breathitt Gray 
1274f1d8a071SWilliam Breathitt Gray module_isa_driver(quad8_driver, num_quad8);
1275f1d8a071SWilliam Breathitt Gray 
1276f1d8a071SWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
1277e357e81fSWilliam Breathitt Gray MODULE_DESCRIPTION("ACCES 104-QUAD-8 driver");
1278f1d8a071SWilliam Breathitt Gray MODULE_LICENSE("GPL v2");
12793216e551SWilliam Breathitt Gray MODULE_IMPORT_NS(COUNTER);
1280