xref: /openbmc/linux/drivers/counter/104-quad-8.c (revision 0c83a280)
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)];
31*0c83a280SWilliam Breathitt Gray static unsigned int num_irq;
32*0c83a280SWilliam Breathitt Gray module_param_hw_array(irq, uint, irq, &num_irq, 0);
337aa2ba0dSWilliam Breathitt Gray MODULE_PARM_DESC(irq, "ACCES 104-QUAD-8 interrupt line numbers");
347aa2ba0dSWilliam Breathitt Gray 
35f1d8a071SWilliam Breathitt Gray #define QUAD8_NUM_COUNTERS 8
36f1d8a071SWilliam Breathitt Gray 
37f1d8a071SWilliam Breathitt Gray /**
38daae1ee5SWilliam Breathitt Gray  * struct channel_reg - channel register structure
39daae1ee5SWilliam Breathitt Gray  * @data:	Count data
40daae1ee5SWilliam Breathitt Gray  * @control:	Channel flags and control
41daae1ee5SWilliam Breathitt Gray  */
42daae1ee5SWilliam Breathitt Gray struct channel_reg {
43daae1ee5SWilliam Breathitt Gray 	u8 data;
44daae1ee5SWilliam Breathitt Gray 	u8 control;
45daae1ee5SWilliam Breathitt Gray };
46daae1ee5SWilliam Breathitt Gray 
47daae1ee5SWilliam Breathitt Gray /**
48daae1ee5SWilliam Breathitt Gray  * struct quad8_reg - device register structure
49daae1ee5SWilliam Breathitt Gray  * @channel:		quadrature counter data and control
50daae1ee5SWilliam Breathitt Gray  * @interrupt_status:	channel interrupt status
51daae1ee5SWilliam Breathitt Gray  * @channel_oper:	enable/reset counters and interrupt functions
52daae1ee5SWilliam Breathitt Gray  * @index_interrupt:	enable channel interrupts
53daae1ee5SWilliam Breathitt Gray  * @reserved:		reserved for Factory Use
54daae1ee5SWilliam Breathitt Gray  * @index_input_levels:	index signal logical input level
55daae1ee5SWilliam Breathitt Gray  * @cable_status:	differential encoder cable status
56daae1ee5SWilliam Breathitt Gray  */
57daae1ee5SWilliam Breathitt Gray struct quad8_reg {
58daae1ee5SWilliam Breathitt Gray 	struct channel_reg channel[QUAD8_NUM_COUNTERS];
59daae1ee5SWilliam Breathitt Gray 	u8 interrupt_status;
60daae1ee5SWilliam Breathitt Gray 	u8 channel_oper;
61daae1ee5SWilliam Breathitt Gray 	u8 index_interrupt;
62daae1ee5SWilliam Breathitt Gray 	u8 reserved[3];
63daae1ee5SWilliam Breathitt Gray 	u8 index_input_levels;
64daae1ee5SWilliam Breathitt Gray 	u8 cable_status;
65daae1ee5SWilliam Breathitt Gray };
66daae1ee5SWilliam Breathitt Gray 
67daae1ee5SWilliam Breathitt Gray /**
68e357e81fSWilliam Breathitt Gray  * struct quad8 - device private data structure
6994a853ecSWilliam Breathitt Gray  * @lock:		lock to prevent clobbering device states during R/W ops
70f1d8a071SWilliam Breathitt Gray  * @counter:		instance of the counter_device
71954ab5ccSWilliam Breathitt Gray  * @fck_prescaler:	array of filter clock prescaler configurations
72f1d8a071SWilliam Breathitt Gray  * @preset:		array of preset values
73f1d8a071SWilliam Breathitt Gray  * @count_mode:		array of count mode configurations
74f1d8a071SWilliam Breathitt Gray  * @quadrature_mode:	array of quadrature mode configurations
75f1d8a071SWilliam Breathitt Gray  * @quadrature_scale:	array of quadrature mode scale configurations
76f1d8a071SWilliam Breathitt Gray  * @ab_enable:		array of A and B inputs enable configurations
77f1d8a071SWilliam Breathitt Gray  * @preset_enable:	array of set_to_preset_on_index attribute configurations
787aa2ba0dSWilliam Breathitt Gray  * @irq_trigger:	array of current IRQ trigger function configurations
79f1d8a071SWilliam Breathitt Gray  * @synchronous_mode:	array of index function synchronous mode configurations
80f1d8a071SWilliam Breathitt Gray  * @index_polarity:	array of index function polarity configurations
81954ab5ccSWilliam Breathitt Gray  * @cable_fault_enable:	differential encoder cable status enable configurations
82daae1ee5SWilliam Breathitt Gray  * @reg:		I/O address offset for the device registers
83f1d8a071SWilliam Breathitt Gray  */
84e357e81fSWilliam Breathitt Gray struct quad8 {
8509db4678SWilliam Breathitt Gray 	spinlock_t lock;
86de65d055SWilliam Breathitt Gray 	unsigned int fck_prescaler[QUAD8_NUM_COUNTERS];
87f1d8a071SWilliam Breathitt Gray 	unsigned int preset[QUAD8_NUM_COUNTERS];
88f1d8a071SWilliam Breathitt Gray 	unsigned int count_mode[QUAD8_NUM_COUNTERS];
89f1d8a071SWilliam Breathitt Gray 	unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
90f1d8a071SWilliam Breathitt Gray 	unsigned int quadrature_scale[QUAD8_NUM_COUNTERS];
91f1d8a071SWilliam Breathitt Gray 	unsigned int ab_enable[QUAD8_NUM_COUNTERS];
92f1d8a071SWilliam Breathitt Gray 	unsigned int preset_enable[QUAD8_NUM_COUNTERS];
937aa2ba0dSWilliam Breathitt Gray 	unsigned int irq_trigger[QUAD8_NUM_COUNTERS];
94f1d8a071SWilliam Breathitt Gray 	unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
95f1d8a071SWilliam Breathitt Gray 	unsigned int index_polarity[QUAD8_NUM_COUNTERS];
96954ab5ccSWilliam Breathitt Gray 	unsigned int cable_fault_enable;
97daae1ee5SWilliam Breathitt Gray 	struct quad8_reg __iomem *reg;
98f1d8a071SWilliam Breathitt Gray };
99f1d8a071SWilliam Breathitt Gray 
100f1d8a071SWilliam Breathitt Gray /* Borrow Toggle flip-flop */
101f1d8a071SWilliam Breathitt Gray #define QUAD8_FLAG_BT BIT(0)
102f1d8a071SWilliam Breathitt Gray /* Carry Toggle flip-flop */
103f1d8a071SWilliam Breathitt Gray #define QUAD8_FLAG_CT BIT(1)
104f1d8a071SWilliam Breathitt Gray /* Error flag */
105f1d8a071SWilliam Breathitt Gray #define QUAD8_FLAG_E BIT(4)
106f1d8a071SWilliam Breathitt Gray /* Up/Down flag */
107f1d8a071SWilliam Breathitt Gray #define QUAD8_FLAG_UD BIT(5)
108f1d8a071SWilliam Breathitt Gray /* Reset and Load Signal Decoders */
109f1d8a071SWilliam Breathitt Gray #define QUAD8_CTR_RLD 0x00
110f1d8a071SWilliam Breathitt Gray /* Counter Mode Register */
111f1d8a071SWilliam Breathitt Gray #define QUAD8_CTR_CMR 0x20
112f1d8a071SWilliam Breathitt Gray /* Input / Output Control Register */
113f1d8a071SWilliam Breathitt Gray #define QUAD8_CTR_IOR 0x40
114f1d8a071SWilliam Breathitt Gray /* Index Control Register */
115f1d8a071SWilliam Breathitt Gray #define QUAD8_CTR_IDR 0x60
116f1d8a071SWilliam Breathitt Gray /* Reset Byte Pointer (three byte data pointer) */
117f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_RESET_BP 0x01
118f1d8a071SWilliam Breathitt Gray /* Reset Counter */
119f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_RESET_CNTR 0x02
120f1d8a071SWilliam Breathitt Gray /* Reset Borrow Toggle, Carry Toggle, Compare Toggle, and Sign flags */
121f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_RESET_FLAGS 0x04
122f1d8a071SWilliam Breathitt Gray /* Reset Error flag */
123f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_RESET_E 0x06
124f1d8a071SWilliam Breathitt Gray /* Preset Register to Counter */
125f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_PRESET_CNTR 0x08
126f1d8a071SWilliam Breathitt Gray /* Transfer Counter to Output Latch */
127f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_CNTR_OUT 0x10
128de65d055SWilliam Breathitt Gray /* Transfer Preset Register LSB to FCK Prescaler */
129de65d055SWilliam Breathitt Gray #define QUAD8_RLD_PRESET_PSC 0x18
130f1d8a071SWilliam Breathitt Gray #define QUAD8_CHAN_OP_RESET_COUNTERS 0x01
1317aa2ba0dSWilliam Breathitt Gray #define QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC 0x04
132f1d8a071SWilliam Breathitt Gray #define QUAD8_CMR_QUADRATURE_X1 0x08
133f1d8a071SWilliam Breathitt Gray #define QUAD8_CMR_QUADRATURE_X2 0x10
134f1d8a071SWilliam Breathitt Gray #define QUAD8_CMR_QUADRATURE_X4 0x18
135f1d8a071SWilliam Breathitt Gray 
136f1d8a071SWilliam Breathitt Gray static int quad8_signal_read(struct counter_device *counter,
137493b938aSWilliam Breathitt Gray 			     struct counter_signal *signal,
138493b938aSWilliam Breathitt Gray 			     enum counter_signal_level *level)
139f1d8a071SWilliam Breathitt Gray {
140aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
141f1d8a071SWilliam Breathitt Gray 	unsigned int state;
142f1d8a071SWilliam Breathitt Gray 
143f1d8a071SWilliam Breathitt Gray 	/* Only Index signal levels can be read */
144f1d8a071SWilliam Breathitt Gray 	if (signal->id < 16)
145f1d8a071SWilliam Breathitt Gray 		return -EINVAL;
146f1d8a071SWilliam Breathitt Gray 
147daae1ee5SWilliam Breathitt Gray 	state = ioread8(&priv->reg->index_input_levels) & BIT(signal->id - 16);
148f1d8a071SWilliam Breathitt Gray 
149493b938aSWilliam Breathitt Gray 	*level = (state) ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW;
150f1d8a071SWilliam Breathitt Gray 
151f1d8a071SWilliam Breathitt Gray 	return 0;
152f1d8a071SWilliam Breathitt Gray }
153f1d8a071SWilliam Breathitt Gray 
154f1d8a071SWilliam Breathitt Gray static int quad8_count_read(struct counter_device *counter,
155aaec1a0fSWilliam Breathitt Gray 			    struct counter_count *count, u64 *val)
156f1d8a071SWilliam Breathitt Gray {
157aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
158daae1ee5SWilliam Breathitt Gray 	struct channel_reg __iomem *const chan = priv->reg->channel + count->id;
159f1d8a071SWilliam Breathitt Gray 	unsigned int flags;
160f1d8a071SWilliam Breathitt Gray 	unsigned int borrow;
161f1d8a071SWilliam Breathitt Gray 	unsigned int carry;
16209db4678SWilliam Breathitt Gray 	unsigned long irqflags;
163f1d8a071SWilliam Breathitt Gray 	int i;
164f1d8a071SWilliam Breathitt Gray 
165daae1ee5SWilliam Breathitt Gray 	flags = ioread8(&chan->control);
166f1d8a071SWilliam Breathitt Gray 	borrow = flags & QUAD8_FLAG_BT;
167f1d8a071SWilliam Breathitt Gray 	carry = !!(flags & QUAD8_FLAG_CT);
168f1d8a071SWilliam Breathitt Gray 
169f1d8a071SWilliam Breathitt Gray 	/* Borrow XOR Carry effectively doubles count range */
170d49e6ee2SWilliam Breathitt Gray 	*val = (unsigned long)(borrow ^ carry) << 24;
171f1d8a071SWilliam Breathitt Gray 
17209db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
173fc069262SSyed Nayyar Waris 
174f1d8a071SWilliam Breathitt Gray 	/* Reset Byte Pointer; transfer Counter to Output Latch */
175b6e9cdedSWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
176daae1ee5SWilliam Breathitt Gray 		 &chan->control);
177f1d8a071SWilliam Breathitt Gray 
178f1d8a071SWilliam Breathitt Gray 	for (i = 0; i < 3; i++)
179daae1ee5SWilliam Breathitt Gray 		*val |= (unsigned long)ioread8(&chan->data) << (8 * i);
180f1d8a071SWilliam Breathitt Gray 
18109db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
182fc069262SSyed Nayyar Waris 
183f1d8a071SWilliam Breathitt Gray 	return 0;
184f1d8a071SWilliam Breathitt Gray }
185f1d8a071SWilliam Breathitt Gray 
186f1d8a071SWilliam Breathitt Gray static int quad8_count_write(struct counter_device *counter,
187aaec1a0fSWilliam Breathitt Gray 			     struct counter_count *count, u64 val)
188f1d8a071SWilliam Breathitt Gray {
189aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
190daae1ee5SWilliam Breathitt Gray 	struct channel_reg __iomem *const chan = priv->reg->channel + count->id;
19109db4678SWilliam Breathitt Gray 	unsigned long irqflags;
192f1d8a071SWilliam Breathitt Gray 	int i;
193f1d8a071SWilliam Breathitt Gray 
194f1d8a071SWilliam Breathitt Gray 	/* Only 24-bit values are supported */
195d49e6ee2SWilliam Breathitt Gray 	if (val > 0xFFFFFF)
196e2ff3198SWilliam Breathitt Gray 		return -ERANGE;
197f1d8a071SWilliam Breathitt Gray 
19809db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
199fc069262SSyed Nayyar Waris 
200f1d8a071SWilliam Breathitt Gray 	/* Reset Byte Pointer */
201daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, &chan->control);
202f1d8a071SWilliam Breathitt Gray 
203f1d8a071SWilliam Breathitt Gray 	/* Counter can only be set via Preset Register */
204f1d8a071SWilliam Breathitt Gray 	for (i = 0; i < 3; i++)
205daae1ee5SWilliam Breathitt Gray 		iowrite8(val >> (8 * i), &chan->data);
206f1d8a071SWilliam Breathitt Gray 
207f1d8a071SWilliam Breathitt Gray 	/* Transfer Preset Register to Counter */
208daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_PRESET_CNTR, &chan->control);
209f1d8a071SWilliam Breathitt Gray 
210f1d8a071SWilliam Breathitt Gray 	/* Reset Byte Pointer */
211daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, &chan->control);
212f1d8a071SWilliam Breathitt Gray 
213f1d8a071SWilliam Breathitt Gray 	/* Set Preset Register back to original value */
214d49e6ee2SWilliam Breathitt Gray 	val = priv->preset[count->id];
215f1d8a071SWilliam Breathitt Gray 	for (i = 0; i < 3; i++)
216daae1ee5SWilliam Breathitt Gray 		iowrite8(val >> (8 * i), &chan->data);
217f1d8a071SWilliam Breathitt Gray 
218f1d8a071SWilliam Breathitt Gray 	/* Reset Borrow, Carry, Compare, and Sign flags */
219daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, &chan->control);
220f1d8a071SWilliam Breathitt Gray 	/* Reset Error flag */
221daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, &chan->control);
222f1d8a071SWilliam Breathitt Gray 
22309db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
224fc069262SSyed Nayyar Waris 
225f1d8a071SWilliam Breathitt Gray 	return 0;
226f1d8a071SWilliam Breathitt Gray }
227f1d8a071SWilliam Breathitt Gray 
228394a0150SWilliam Breathitt Gray static const enum counter_function quad8_count_functions_list[] = {
229aaec1a0fSWilliam Breathitt Gray 	COUNTER_FUNCTION_PULSE_DIRECTION,
230aaec1a0fSWilliam Breathitt Gray 	COUNTER_FUNCTION_QUADRATURE_X1_A,
231aaec1a0fSWilliam Breathitt Gray 	COUNTER_FUNCTION_QUADRATURE_X2_A,
232aaec1a0fSWilliam Breathitt Gray 	COUNTER_FUNCTION_QUADRATURE_X4,
233f1d8a071SWilliam Breathitt Gray };
234f1d8a071SWilliam Breathitt Gray 
235aaec1a0fSWilliam Breathitt Gray static int quad8_function_read(struct counter_device *counter,
236aaec1a0fSWilliam Breathitt Gray 			       struct counter_count *count,
237aaec1a0fSWilliam Breathitt Gray 			       enum counter_function *function)
238f1d8a071SWilliam Breathitt Gray {
239aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
240f1d8a071SWilliam Breathitt Gray 	const int id = count->id;
24109db4678SWilliam Breathitt Gray 	unsigned long irqflags;
242f1d8a071SWilliam Breathitt Gray 
24309db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
244fc069262SSyed Nayyar Waris 
245fc069262SSyed Nayyar Waris 	if (priv->quadrature_mode[id])
246fc069262SSyed Nayyar Waris 		switch (priv->quadrature_scale[id]) {
247f1d8a071SWilliam Breathitt Gray 		case 0:
248aaec1a0fSWilliam Breathitt Gray 			*function = COUNTER_FUNCTION_QUADRATURE_X1_A;
249f1d8a071SWilliam Breathitt Gray 			break;
250f1d8a071SWilliam Breathitt Gray 		case 1:
251aaec1a0fSWilliam Breathitt Gray 			*function = COUNTER_FUNCTION_QUADRATURE_X2_A;
252f1d8a071SWilliam Breathitt Gray 			break;
253f1d8a071SWilliam Breathitt Gray 		case 2:
254aaec1a0fSWilliam Breathitt Gray 			*function = COUNTER_FUNCTION_QUADRATURE_X4;
255f1d8a071SWilliam Breathitt Gray 			break;
256f1d8a071SWilliam Breathitt Gray 		}
257f1d8a071SWilliam Breathitt Gray 	else
258aaec1a0fSWilliam Breathitt Gray 		*function = COUNTER_FUNCTION_PULSE_DIRECTION;
259f1d8a071SWilliam Breathitt Gray 
26009db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
261fc069262SSyed Nayyar Waris 
262f1d8a071SWilliam Breathitt Gray 	return 0;
263f1d8a071SWilliam Breathitt Gray }
264f1d8a071SWilliam Breathitt Gray 
265aaec1a0fSWilliam Breathitt Gray static int quad8_function_write(struct counter_device *counter,
266aaec1a0fSWilliam Breathitt Gray 				struct counter_count *count,
267aaec1a0fSWilliam Breathitt Gray 				enum counter_function function)
268f1d8a071SWilliam Breathitt Gray {
269aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
270f1d8a071SWilliam Breathitt Gray 	const int id = count->id;
271f1d8a071SWilliam Breathitt Gray 	unsigned int *const quadrature_mode = priv->quadrature_mode + id;
272f1d8a071SWilliam Breathitt Gray 	unsigned int *const scale = priv->quadrature_scale + id;
273f1d8a071SWilliam Breathitt Gray 	unsigned int *const synchronous_mode = priv->synchronous_mode + id;
274daae1ee5SWilliam Breathitt Gray 	u8 __iomem *const control = &priv->reg->channel[id].control;
27509db4678SWilliam Breathitt Gray 	unsigned long irqflags;
276fc069262SSyed Nayyar Waris 	unsigned int mode_cfg;
277fc069262SSyed Nayyar Waris 	unsigned int idr_cfg;
278fc069262SSyed Nayyar Waris 
27909db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
280fc069262SSyed Nayyar Waris 
281fc069262SSyed Nayyar Waris 	mode_cfg = priv->count_mode[id] << 1;
282fc069262SSyed Nayyar Waris 	idr_cfg = priv->index_polarity[id] << 1;
283f1d8a071SWilliam Breathitt Gray 
284aaec1a0fSWilliam Breathitt Gray 	if (function == COUNTER_FUNCTION_PULSE_DIRECTION) {
285f1d8a071SWilliam Breathitt Gray 		*quadrature_mode = 0;
286f1d8a071SWilliam Breathitt Gray 
287f1d8a071SWilliam Breathitt Gray 		/* Quadrature scaling only available in quadrature mode */
288f1d8a071SWilliam Breathitt Gray 		*scale = 0;
289f1d8a071SWilliam Breathitt Gray 
290f1d8a071SWilliam Breathitt Gray 		/* Synchronous function not supported in non-quadrature mode */
291f1d8a071SWilliam Breathitt Gray 		if (*synchronous_mode) {
292f1d8a071SWilliam Breathitt Gray 			*synchronous_mode = 0;
293f1d8a071SWilliam Breathitt Gray 			/* Disable synchronous function mode */
294daae1ee5SWilliam Breathitt Gray 			iowrite8(QUAD8_CTR_IDR | idr_cfg, control);
295f1d8a071SWilliam Breathitt Gray 		}
296f1d8a071SWilliam Breathitt Gray 	} else {
297f1d8a071SWilliam Breathitt Gray 		*quadrature_mode = 1;
298f1d8a071SWilliam Breathitt Gray 
299f1d8a071SWilliam Breathitt Gray 		switch (function) {
300aaec1a0fSWilliam Breathitt Gray 		case COUNTER_FUNCTION_QUADRATURE_X1_A:
301f1d8a071SWilliam Breathitt Gray 			*scale = 0;
302f1d8a071SWilliam Breathitt Gray 			mode_cfg |= QUAD8_CMR_QUADRATURE_X1;
303f1d8a071SWilliam Breathitt Gray 			break;
304aaec1a0fSWilliam Breathitt Gray 		case COUNTER_FUNCTION_QUADRATURE_X2_A:
305f1d8a071SWilliam Breathitt Gray 			*scale = 1;
306f1d8a071SWilliam Breathitt Gray 			mode_cfg |= QUAD8_CMR_QUADRATURE_X2;
307f1d8a071SWilliam Breathitt Gray 			break;
308aaec1a0fSWilliam Breathitt Gray 		case COUNTER_FUNCTION_QUADRATURE_X4:
309f1d8a071SWilliam Breathitt Gray 			*scale = 2;
310f1d8a071SWilliam Breathitt Gray 			mode_cfg |= QUAD8_CMR_QUADRATURE_X4;
311f1d8a071SWilliam Breathitt Gray 			break;
312b11eed15SWilliam Breathitt Gray 		default:
313b11eed15SWilliam Breathitt Gray 			/* should never reach this path */
31409db4678SWilliam Breathitt Gray 			spin_unlock_irqrestore(&priv->lock, irqflags);
315b11eed15SWilliam Breathitt Gray 			return -EINVAL;
316f1d8a071SWilliam Breathitt Gray 		}
317f1d8a071SWilliam Breathitt Gray 	}
318f1d8a071SWilliam Breathitt Gray 
319f1d8a071SWilliam Breathitt Gray 	/* Load mode configuration to Counter Mode Register */
320daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_CMR | mode_cfg, control);
321f1d8a071SWilliam Breathitt Gray 
32209db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
323fc069262SSyed Nayyar Waris 
324f1d8a071SWilliam Breathitt Gray 	return 0;
325f1d8a071SWilliam Breathitt Gray }
326f1d8a071SWilliam Breathitt Gray 
327aaec1a0fSWilliam Breathitt Gray static int quad8_direction_read(struct counter_device *counter,
328aaec1a0fSWilliam Breathitt Gray 				struct counter_count *count,
329aaec1a0fSWilliam Breathitt Gray 				enum counter_count_direction *direction)
330f1d8a071SWilliam Breathitt Gray {
331aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
332f1d8a071SWilliam Breathitt Gray 	unsigned int ud_flag;
333daae1ee5SWilliam Breathitt Gray 	u8 __iomem *const flag_addr = &priv->reg->channel[count->id].control;
334f1d8a071SWilliam Breathitt Gray 
335f1d8a071SWilliam Breathitt Gray 	/* U/D flag: nonzero = up, zero = down */
336b6e9cdedSWilliam Breathitt Gray 	ud_flag = ioread8(flag_addr) & QUAD8_FLAG_UD;
337f1d8a071SWilliam Breathitt Gray 
338f1d8a071SWilliam Breathitt Gray 	*direction = (ud_flag) ? COUNTER_COUNT_DIRECTION_FORWARD :
339f1d8a071SWilliam Breathitt Gray 		COUNTER_COUNT_DIRECTION_BACKWARD;
340aaec1a0fSWilliam Breathitt Gray 
341aaec1a0fSWilliam Breathitt Gray 	return 0;
342f1d8a071SWilliam Breathitt Gray }
343f1d8a071SWilliam Breathitt Gray 
3446a9eb0e3SWilliam Breathitt Gray static const enum counter_synapse_action quad8_index_actions_list[] = {
345aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_NONE,
346aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
347f1d8a071SWilliam Breathitt Gray };
348f1d8a071SWilliam Breathitt Gray 
3496a9eb0e3SWilliam Breathitt Gray static const enum counter_synapse_action quad8_synapse_actions_list[] = {
350aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_NONE,
351aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
352aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
353aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
354f1d8a071SWilliam Breathitt Gray };
355f1d8a071SWilliam Breathitt Gray 
356aaec1a0fSWilliam Breathitt Gray static int quad8_action_read(struct counter_device *counter,
357aaec1a0fSWilliam Breathitt Gray 			     struct counter_count *count,
358aaec1a0fSWilliam Breathitt Gray 			     struct counter_synapse *synapse,
359aaec1a0fSWilliam Breathitt Gray 			     enum counter_synapse_action *action)
360f1d8a071SWilliam Breathitt Gray {
361aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
362f1d8a071SWilliam Breathitt Gray 	int err;
363aaec1a0fSWilliam Breathitt Gray 	enum counter_function function;
364f1d8a071SWilliam Breathitt Gray 	const size_t signal_a_id = count->synapses[0].signal->id;
365f1d8a071SWilliam Breathitt Gray 	enum counter_count_direction direction;
366f1d8a071SWilliam Breathitt Gray 
367f1d8a071SWilliam Breathitt Gray 	/* Handle Index signals */
368f1d8a071SWilliam Breathitt Gray 	if (synapse->signal->id >= 16) {
369f1d8a071SWilliam Breathitt Gray 		if (priv->preset_enable[count->id])
370aaec1a0fSWilliam Breathitt Gray 			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
371f1d8a071SWilliam Breathitt Gray 		else
372aaec1a0fSWilliam Breathitt Gray 			*action = COUNTER_SYNAPSE_ACTION_NONE;
373f1d8a071SWilliam Breathitt Gray 
374f1d8a071SWilliam Breathitt Gray 		return 0;
375f1d8a071SWilliam Breathitt Gray 	}
376f1d8a071SWilliam Breathitt Gray 
377aaec1a0fSWilliam Breathitt Gray 	err = quad8_function_read(counter, count, &function);
378f1d8a071SWilliam Breathitt Gray 	if (err)
379f1d8a071SWilliam Breathitt Gray 		return err;
380f1d8a071SWilliam Breathitt Gray 
381f1d8a071SWilliam Breathitt Gray 	/* Default action mode */
382aaec1a0fSWilliam Breathitt Gray 	*action = COUNTER_SYNAPSE_ACTION_NONE;
383f1d8a071SWilliam Breathitt Gray 
384f1d8a071SWilliam Breathitt Gray 	/* Determine action mode based on current count function mode */
385f1d8a071SWilliam Breathitt Gray 	switch (function) {
386aaec1a0fSWilliam Breathitt Gray 	case COUNTER_FUNCTION_PULSE_DIRECTION:
387f1d8a071SWilliam Breathitt Gray 		if (synapse->signal->id == signal_a_id)
388aaec1a0fSWilliam Breathitt Gray 			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
389b11eed15SWilliam Breathitt Gray 		return 0;
390aaec1a0fSWilliam Breathitt Gray 	case COUNTER_FUNCTION_QUADRATURE_X1_A:
391f1d8a071SWilliam Breathitt Gray 		if (synapse->signal->id == signal_a_id) {
392aaec1a0fSWilliam Breathitt Gray 			err = quad8_direction_read(counter, count, &direction);
393aaec1a0fSWilliam Breathitt Gray 			if (err)
394aaec1a0fSWilliam Breathitt Gray 				return err;
395f1d8a071SWilliam Breathitt Gray 
396f1d8a071SWilliam Breathitt Gray 			if (direction == COUNTER_COUNT_DIRECTION_FORWARD)
397aaec1a0fSWilliam Breathitt Gray 				*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
398f1d8a071SWilliam Breathitt Gray 			else
399aaec1a0fSWilliam Breathitt Gray 				*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
400f1d8a071SWilliam Breathitt Gray 		}
401b11eed15SWilliam Breathitt Gray 		return 0;
402aaec1a0fSWilliam Breathitt Gray 	case COUNTER_FUNCTION_QUADRATURE_X2_A:
403f1d8a071SWilliam Breathitt Gray 		if (synapse->signal->id == signal_a_id)
404aaec1a0fSWilliam Breathitt Gray 			*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
405b11eed15SWilliam Breathitt Gray 		return 0;
406aaec1a0fSWilliam Breathitt Gray 	case COUNTER_FUNCTION_QUADRATURE_X4:
407aaec1a0fSWilliam Breathitt Gray 		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
408f1d8a071SWilliam Breathitt Gray 		return 0;
409b11eed15SWilliam Breathitt Gray 	default:
410b11eed15SWilliam Breathitt Gray 		/* should never reach this path */
411b11eed15SWilliam Breathitt Gray 		return -EINVAL;
412b11eed15SWilliam Breathitt Gray 	}
413f1d8a071SWilliam Breathitt Gray }
414f1d8a071SWilliam Breathitt Gray 
4157aa2ba0dSWilliam Breathitt Gray enum {
4167aa2ba0dSWilliam Breathitt Gray 	QUAD8_EVENT_CARRY = 0,
4177aa2ba0dSWilliam Breathitt Gray 	QUAD8_EVENT_COMPARE = 1,
4187aa2ba0dSWilliam Breathitt Gray 	QUAD8_EVENT_CARRY_BORROW = 2,
4197aa2ba0dSWilliam Breathitt Gray 	QUAD8_EVENT_INDEX = 3,
4207aa2ba0dSWilliam Breathitt Gray };
4217aa2ba0dSWilliam Breathitt Gray 
4227aa2ba0dSWilliam Breathitt Gray static int quad8_events_configure(struct counter_device *counter)
4237aa2ba0dSWilliam Breathitt Gray {
424aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
4257aa2ba0dSWilliam Breathitt Gray 	unsigned long irq_enabled = 0;
4267aa2ba0dSWilliam Breathitt Gray 	unsigned long irqflags;
427c95cc0d9SWilliam Breathitt Gray 	struct counter_event_node *event_node;
428c95cc0d9SWilliam Breathitt Gray 	unsigned int next_irq_trigger;
4297aa2ba0dSWilliam Breathitt Gray 	unsigned long ior_cfg;
4307aa2ba0dSWilliam Breathitt Gray 
4317aa2ba0dSWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
4327aa2ba0dSWilliam Breathitt Gray 
433c95cc0d9SWilliam Breathitt Gray 	list_for_each_entry(event_node, &counter->events_list, l) {
434c95cc0d9SWilliam Breathitt Gray 		switch (event_node->event) {
435c95cc0d9SWilliam Breathitt Gray 		case COUNTER_EVENT_OVERFLOW:
436c95cc0d9SWilliam Breathitt Gray 			next_irq_trigger = QUAD8_EVENT_CARRY;
437c95cc0d9SWilliam Breathitt Gray 			break;
438c95cc0d9SWilliam Breathitt Gray 		case COUNTER_EVENT_THRESHOLD:
439c95cc0d9SWilliam Breathitt Gray 			next_irq_trigger = QUAD8_EVENT_COMPARE;
440c95cc0d9SWilliam Breathitt Gray 			break;
441c95cc0d9SWilliam Breathitt Gray 		case COUNTER_EVENT_OVERFLOW_UNDERFLOW:
442c95cc0d9SWilliam Breathitt Gray 			next_irq_trigger = QUAD8_EVENT_CARRY_BORROW;
443c95cc0d9SWilliam Breathitt Gray 			break;
444c95cc0d9SWilliam Breathitt Gray 		case COUNTER_EVENT_INDEX:
445c95cc0d9SWilliam Breathitt Gray 			next_irq_trigger = QUAD8_EVENT_INDEX;
446c95cc0d9SWilliam Breathitt Gray 			break;
447c95cc0d9SWilliam Breathitt Gray 		default:
448c95cc0d9SWilliam Breathitt Gray 			/* should never reach this path */
449c95cc0d9SWilliam Breathitt Gray 			spin_unlock_irqrestore(&priv->lock, irqflags);
450c95cc0d9SWilliam Breathitt Gray 			return -EINVAL;
4517aa2ba0dSWilliam Breathitt Gray 		}
4527aa2ba0dSWilliam Breathitt Gray 
453c95cc0d9SWilliam Breathitt Gray 		/* Skip configuration if it is the same as previously set */
454c95cc0d9SWilliam Breathitt Gray 		if (priv->irq_trigger[event_node->channel] == next_irq_trigger)
455c95cc0d9SWilliam Breathitt Gray 			continue;
456c95cc0d9SWilliam Breathitt Gray 
457c95cc0d9SWilliam Breathitt Gray 		/* Save new IRQ function configuration */
458c95cc0d9SWilliam Breathitt Gray 		priv->irq_trigger[event_node->channel] = next_irq_trigger;
459c95cc0d9SWilliam Breathitt Gray 
460c95cc0d9SWilliam Breathitt Gray 		/* Load configuration to I/O Control Register */
461c95cc0d9SWilliam Breathitt Gray 		ior_cfg = priv->ab_enable[event_node->channel] |
462c95cc0d9SWilliam Breathitt Gray 			  priv->preset_enable[event_node->channel] << 1 |
463c95cc0d9SWilliam Breathitt Gray 			  priv->irq_trigger[event_node->channel] << 3;
464daae1ee5SWilliam Breathitt Gray 		iowrite8(QUAD8_CTR_IOR | ior_cfg,
465daae1ee5SWilliam Breathitt Gray 			 &priv->reg->channel[event_node->channel].control);
4667aa2ba0dSWilliam Breathitt Gray 
4677aa2ba0dSWilliam Breathitt Gray 		/* Enable IRQ line */
468c95cc0d9SWilliam Breathitt Gray 		irq_enabled |= BIT(event_node->channel);
4697aa2ba0dSWilliam Breathitt Gray 	}
4707aa2ba0dSWilliam Breathitt Gray 
471daae1ee5SWilliam Breathitt Gray 	iowrite8(irq_enabled, &priv->reg->index_interrupt);
4727aa2ba0dSWilliam Breathitt Gray 
4737aa2ba0dSWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
4747aa2ba0dSWilliam Breathitt Gray 
4757aa2ba0dSWilliam Breathitt Gray 	return 0;
4767aa2ba0dSWilliam Breathitt Gray }
4777aa2ba0dSWilliam Breathitt Gray 
4787aa2ba0dSWilliam Breathitt Gray static int quad8_watch_validate(struct counter_device *counter,
4797aa2ba0dSWilliam Breathitt Gray 				const struct counter_watch *watch)
4807aa2ba0dSWilliam Breathitt Gray {
481c95cc0d9SWilliam Breathitt Gray 	struct counter_event_node *event_node;
4827aa2ba0dSWilliam Breathitt Gray 
4837aa2ba0dSWilliam Breathitt Gray 	if (watch->channel > QUAD8_NUM_COUNTERS - 1)
4847aa2ba0dSWilliam Breathitt Gray 		return -EINVAL;
4857aa2ba0dSWilliam Breathitt Gray 
4867aa2ba0dSWilliam Breathitt Gray 	switch (watch->event) {
4877aa2ba0dSWilliam Breathitt Gray 	case COUNTER_EVENT_OVERFLOW:
4887aa2ba0dSWilliam Breathitt Gray 	case COUNTER_EVENT_THRESHOLD:
4897aa2ba0dSWilliam Breathitt Gray 	case COUNTER_EVENT_OVERFLOW_UNDERFLOW:
4907aa2ba0dSWilliam Breathitt Gray 	case COUNTER_EVENT_INDEX:
491c95cc0d9SWilliam Breathitt Gray 		list_for_each_entry(event_node, &counter->next_events_list, l)
492c95cc0d9SWilliam Breathitt Gray 			if (watch->channel == event_node->channel &&
493c95cc0d9SWilliam Breathitt Gray 				watch->event != event_node->event)
4947aa2ba0dSWilliam Breathitt Gray 				return -EINVAL;
4957aa2ba0dSWilliam Breathitt Gray 		return 0;
4967aa2ba0dSWilliam Breathitt Gray 	default:
4977aa2ba0dSWilliam Breathitt Gray 		return -EINVAL;
4987aa2ba0dSWilliam Breathitt Gray 	}
4997aa2ba0dSWilliam Breathitt Gray }
5007aa2ba0dSWilliam Breathitt Gray 
50117aa207eSYueHaibing static const struct counter_ops quad8_ops = {
502f1d8a071SWilliam Breathitt Gray 	.signal_read = quad8_signal_read,
503f1d8a071SWilliam Breathitt Gray 	.count_read = quad8_count_read,
504f1d8a071SWilliam Breathitt Gray 	.count_write = quad8_count_write,
505aaec1a0fSWilliam Breathitt Gray 	.function_read = quad8_function_read,
506aaec1a0fSWilliam Breathitt Gray 	.function_write = quad8_function_write,
5077aa2ba0dSWilliam Breathitt Gray 	.action_read = quad8_action_read,
5087aa2ba0dSWilliam Breathitt Gray 	.events_configure = quad8_events_configure,
5097aa2ba0dSWilliam Breathitt Gray 	.watch_validate = quad8_watch_validate,
510f1d8a071SWilliam Breathitt Gray };
511f1d8a071SWilliam Breathitt Gray 
512e357e81fSWilliam Breathitt Gray static const char *const quad8_index_polarity_modes[] = {
513e357e81fSWilliam Breathitt Gray 	"negative",
514e357e81fSWilliam Breathitt Gray 	"positive"
515e357e81fSWilliam Breathitt Gray };
516e357e81fSWilliam Breathitt Gray 
517f1d8a071SWilliam Breathitt Gray static int quad8_index_polarity_get(struct counter_device *counter,
518aaec1a0fSWilliam Breathitt Gray 				    struct counter_signal *signal,
519aaec1a0fSWilliam Breathitt Gray 				    u32 *index_polarity)
520f1d8a071SWilliam Breathitt Gray {
521aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
522f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
523f1d8a071SWilliam Breathitt Gray 
524f1d8a071SWilliam Breathitt Gray 	*index_polarity = priv->index_polarity[channel_id];
525f1d8a071SWilliam Breathitt Gray 
526f1d8a071SWilliam Breathitt Gray 	return 0;
527f1d8a071SWilliam Breathitt Gray }
528f1d8a071SWilliam Breathitt Gray 
529f1d8a071SWilliam Breathitt Gray static int quad8_index_polarity_set(struct counter_device *counter,
530aaec1a0fSWilliam Breathitt Gray 				    struct counter_signal *signal,
531aaec1a0fSWilliam Breathitt Gray 				    u32 index_polarity)
532f1d8a071SWilliam Breathitt Gray {
533aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
534f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
535daae1ee5SWilliam Breathitt Gray 	u8 __iomem *const control = &priv->reg->channel[channel_id].control;
53609db4678SWilliam Breathitt Gray 	unsigned long irqflags;
537fc069262SSyed Nayyar Waris 	unsigned int idr_cfg = index_polarity << 1;
538fc069262SSyed Nayyar Waris 
53909db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
540fc069262SSyed Nayyar Waris 
541fc069262SSyed Nayyar Waris 	idr_cfg |= priv->synchronous_mode[channel_id];
542f1d8a071SWilliam Breathitt Gray 
543f1d8a071SWilliam Breathitt Gray 	priv->index_polarity[channel_id] = index_polarity;
544f1d8a071SWilliam Breathitt Gray 
545f1d8a071SWilliam Breathitt Gray 	/* Load Index Control configuration to Index Control Register */
546daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_IDR | idr_cfg, control);
547f1d8a071SWilliam Breathitt Gray 
54809db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
549fc069262SSyed Nayyar Waris 
550f1d8a071SWilliam Breathitt Gray 	return 0;
551f1d8a071SWilliam Breathitt Gray }
552f1d8a071SWilliam Breathitt Gray 
553e357e81fSWilliam Breathitt Gray static const char *const quad8_synchronous_modes[] = {
554e357e81fSWilliam Breathitt Gray 	"non-synchronous",
555e357e81fSWilliam Breathitt Gray 	"synchronous"
556e357e81fSWilliam Breathitt Gray };
557e357e81fSWilliam Breathitt Gray 
558f1d8a071SWilliam Breathitt Gray static int quad8_synchronous_mode_get(struct counter_device *counter,
559aaec1a0fSWilliam Breathitt Gray 				      struct counter_signal *signal,
560aaec1a0fSWilliam Breathitt Gray 				      u32 *synchronous_mode)
561f1d8a071SWilliam Breathitt Gray {
562aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
563f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
564f1d8a071SWilliam Breathitt Gray 
565f1d8a071SWilliam Breathitt Gray 	*synchronous_mode = priv->synchronous_mode[channel_id];
566f1d8a071SWilliam Breathitt Gray 
567f1d8a071SWilliam Breathitt Gray 	return 0;
568f1d8a071SWilliam Breathitt Gray }
569f1d8a071SWilliam Breathitt Gray 
570f1d8a071SWilliam Breathitt Gray static int quad8_synchronous_mode_set(struct counter_device *counter,
571aaec1a0fSWilliam Breathitt Gray 				      struct counter_signal *signal,
572aaec1a0fSWilliam Breathitt Gray 				      u32 synchronous_mode)
573f1d8a071SWilliam Breathitt Gray {
574aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
575f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
576daae1ee5SWilliam Breathitt Gray 	u8 __iomem *const control = &priv->reg->channel[channel_id].control;
57709db4678SWilliam Breathitt Gray 	unsigned long irqflags;
578fc069262SSyed Nayyar Waris 	unsigned int idr_cfg = synchronous_mode;
579fc069262SSyed Nayyar Waris 
58009db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
581fc069262SSyed Nayyar Waris 
582fc069262SSyed Nayyar Waris 	idr_cfg |= priv->index_polarity[channel_id] << 1;
583f1d8a071SWilliam Breathitt Gray 
584f1d8a071SWilliam Breathitt Gray 	/* Index function must be non-synchronous in non-quadrature mode */
585fc069262SSyed Nayyar Waris 	if (synchronous_mode && !priv->quadrature_mode[channel_id]) {
58609db4678SWilliam Breathitt Gray 		spin_unlock_irqrestore(&priv->lock, irqflags);
587f1d8a071SWilliam Breathitt Gray 		return -EINVAL;
588fc069262SSyed Nayyar Waris 	}
589f1d8a071SWilliam Breathitt Gray 
590f1d8a071SWilliam Breathitt Gray 	priv->synchronous_mode[channel_id] = synchronous_mode;
591f1d8a071SWilliam Breathitt Gray 
592f1d8a071SWilliam Breathitt Gray 	/* Load Index Control configuration to Index Control Register */
593daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_IDR | idr_cfg, control);
594f1d8a071SWilliam Breathitt Gray 
59509db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
596fc069262SSyed Nayyar Waris 
597f1d8a071SWilliam Breathitt Gray 	return 0;
598f1d8a071SWilliam Breathitt Gray }
599f1d8a071SWilliam Breathitt Gray 
600aaec1a0fSWilliam Breathitt Gray static int quad8_count_floor_read(struct counter_device *counter,
601aaec1a0fSWilliam Breathitt Gray 				  struct counter_count *count, u64 *floor)
602f1d8a071SWilliam Breathitt Gray {
603f1d8a071SWilliam Breathitt Gray 	/* Only a floor of 0 is supported */
604aaec1a0fSWilliam Breathitt Gray 	*floor = 0;
605aaec1a0fSWilliam Breathitt Gray 
606aaec1a0fSWilliam Breathitt Gray 	return 0;
607f1d8a071SWilliam Breathitt Gray }
608f1d8a071SWilliam Breathitt Gray 
609aaec1a0fSWilliam Breathitt Gray static int quad8_count_mode_read(struct counter_device *counter,
610aaec1a0fSWilliam Breathitt Gray 				 struct counter_count *count,
611aaec1a0fSWilliam Breathitt Gray 				 enum counter_count_mode *cnt_mode)
612f1d8a071SWilliam Breathitt Gray {
613aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
614f1d8a071SWilliam Breathitt Gray 
615f1d8a071SWilliam Breathitt Gray 	/* Map 104-QUAD-8 count mode to Generic Counter count mode */
616f1d8a071SWilliam Breathitt Gray 	switch (priv->count_mode[count->id]) {
617f1d8a071SWilliam Breathitt Gray 	case 0:
618f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_NORMAL;
619f1d8a071SWilliam Breathitt Gray 		break;
620f1d8a071SWilliam Breathitt Gray 	case 1:
621f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_RANGE_LIMIT;
622f1d8a071SWilliam Breathitt Gray 		break;
623f1d8a071SWilliam Breathitt Gray 	case 2:
624f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_NON_RECYCLE;
625f1d8a071SWilliam Breathitt Gray 		break;
626f1d8a071SWilliam Breathitt Gray 	case 3:
627f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_MODULO_N;
628f1d8a071SWilliam Breathitt Gray 		break;
629f1d8a071SWilliam Breathitt Gray 	}
630f1d8a071SWilliam Breathitt Gray 
631f1d8a071SWilliam Breathitt Gray 	return 0;
632f1d8a071SWilliam Breathitt Gray }
633f1d8a071SWilliam Breathitt Gray 
634aaec1a0fSWilliam Breathitt Gray static int quad8_count_mode_write(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 	struct quad8 *const priv = counter_priv(counter);
639aaec1a0fSWilliam Breathitt Gray 	unsigned int count_mode;
640f1d8a071SWilliam Breathitt Gray 	unsigned int mode_cfg;
641daae1ee5SWilliam Breathitt Gray 	u8 __iomem *const control = &priv->reg->channel[count->id].control;
64209db4678SWilliam Breathitt Gray 	unsigned long irqflags;
643f1d8a071SWilliam Breathitt Gray 
644f1d8a071SWilliam Breathitt Gray 	/* Map Generic Counter count mode to 104-QUAD-8 count mode */
645f1d8a071SWilliam Breathitt Gray 	switch (cnt_mode) {
646f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_NORMAL:
647aaec1a0fSWilliam Breathitt Gray 		count_mode = 0;
648f1d8a071SWilliam Breathitt Gray 		break;
649f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_RANGE_LIMIT:
650aaec1a0fSWilliam Breathitt Gray 		count_mode = 1;
651f1d8a071SWilliam Breathitt Gray 		break;
652f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_NON_RECYCLE:
653aaec1a0fSWilliam Breathitt Gray 		count_mode = 2;
654f1d8a071SWilliam Breathitt Gray 		break;
655f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_MODULO_N:
656aaec1a0fSWilliam Breathitt Gray 		count_mode = 3;
657f1d8a071SWilliam Breathitt Gray 		break;
658b11eed15SWilliam Breathitt Gray 	default:
659b11eed15SWilliam Breathitt Gray 		/* should never reach this path */
660b11eed15SWilliam Breathitt Gray 		return -EINVAL;
661f1d8a071SWilliam Breathitt Gray 	}
662f1d8a071SWilliam Breathitt Gray 
66309db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
664fc069262SSyed Nayyar Waris 
665aaec1a0fSWilliam Breathitt Gray 	priv->count_mode[count->id] = count_mode;
666f1d8a071SWilliam Breathitt Gray 
667f1d8a071SWilliam Breathitt Gray 	/* Set count mode configuration value */
668aaec1a0fSWilliam Breathitt Gray 	mode_cfg = count_mode << 1;
669f1d8a071SWilliam Breathitt Gray 
670f1d8a071SWilliam Breathitt Gray 	/* Add quadrature mode configuration */
671f1d8a071SWilliam Breathitt Gray 	if (priv->quadrature_mode[count->id])
672f1d8a071SWilliam Breathitt Gray 		mode_cfg |= (priv->quadrature_scale[count->id] + 1) << 3;
673f1d8a071SWilliam Breathitt Gray 
674f1d8a071SWilliam Breathitt Gray 	/* Load mode configuration to Counter Mode Register */
675daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_CMR | mode_cfg, control);
676f1d8a071SWilliam Breathitt Gray 
67709db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
678fc069262SSyed Nayyar Waris 
679f1d8a071SWilliam Breathitt Gray 	return 0;
680f1d8a071SWilliam Breathitt Gray }
681f1d8a071SWilliam Breathitt Gray 
682aaec1a0fSWilliam Breathitt Gray static int quad8_count_enable_read(struct counter_device *counter,
683aaec1a0fSWilliam Breathitt Gray 				   struct counter_count *count, u8 *enable)
684f1d8a071SWilliam Breathitt Gray {
685aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
686f1d8a071SWilliam Breathitt Gray 
687aaec1a0fSWilliam Breathitt Gray 	*enable = priv->ab_enable[count->id];
688aaec1a0fSWilliam Breathitt Gray 
689aaec1a0fSWilliam Breathitt Gray 	return 0;
690f1d8a071SWilliam Breathitt Gray }
691f1d8a071SWilliam Breathitt Gray 
692aaec1a0fSWilliam Breathitt Gray static int quad8_count_enable_write(struct counter_device *counter,
693aaec1a0fSWilliam Breathitt Gray 				    struct counter_count *count, u8 enable)
694f1d8a071SWilliam Breathitt Gray {
695aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
696daae1ee5SWilliam Breathitt Gray 	u8 __iomem *const control = &priv->reg->channel[count->id].control;
69709db4678SWilliam Breathitt Gray 	unsigned long irqflags;
698f1d8a071SWilliam Breathitt Gray 	unsigned int ior_cfg;
699f1d8a071SWilliam Breathitt Gray 
70009db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
701fc069262SSyed Nayyar Waris 
702aaec1a0fSWilliam Breathitt Gray 	priv->ab_enable[count->id] = enable;
703f1d8a071SWilliam Breathitt Gray 
7047aa2ba0dSWilliam Breathitt Gray 	ior_cfg = enable | priv->preset_enable[count->id] << 1 |
7057aa2ba0dSWilliam Breathitt Gray 		  priv->irq_trigger[count->id] << 3;
706f1d8a071SWilliam Breathitt Gray 
707f1d8a071SWilliam Breathitt Gray 	/* Load I/O control configuration */
708daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_IOR | ior_cfg, control);
709f1d8a071SWilliam Breathitt Gray 
71009db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
711fc069262SSyed Nayyar Waris 
712aaec1a0fSWilliam Breathitt Gray 	return 0;
713f1d8a071SWilliam Breathitt Gray }
714f1d8a071SWilliam Breathitt Gray 
715e357e81fSWilliam Breathitt Gray static const char *const quad8_noise_error_states[] = {
716e357e81fSWilliam Breathitt Gray 	"No excessive noise is present at the count inputs",
717e357e81fSWilliam Breathitt Gray 	"Excessive noise is present at the count inputs"
718e357e81fSWilliam Breathitt Gray };
719e357e81fSWilliam Breathitt Gray 
720f1d8a071SWilliam Breathitt Gray static int quad8_error_noise_get(struct counter_device *counter,
721aaec1a0fSWilliam Breathitt Gray 				 struct counter_count *count, u32 *noise_error)
722f1d8a071SWilliam Breathitt Gray {
723aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
724daae1ee5SWilliam Breathitt Gray 	u8 __iomem *const flag_addr = &priv->reg->channel[count->id].control;
725f1d8a071SWilliam Breathitt Gray 
726daae1ee5SWilliam Breathitt Gray 	*noise_error = !!(ioread8(flag_addr) & QUAD8_FLAG_E);
727f1d8a071SWilliam Breathitt Gray 
728f1d8a071SWilliam Breathitt Gray 	return 0;
729f1d8a071SWilliam Breathitt Gray }
730f1d8a071SWilliam Breathitt Gray 
731aaec1a0fSWilliam Breathitt Gray static int quad8_count_preset_read(struct counter_device *counter,
732aaec1a0fSWilliam Breathitt Gray 				   struct counter_count *count, u64 *preset)
733f1d8a071SWilliam Breathitt Gray {
734aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
735f1d8a071SWilliam Breathitt Gray 
736aaec1a0fSWilliam Breathitt Gray 	*preset = priv->preset[count->id];
737aaec1a0fSWilliam Breathitt Gray 
738aaec1a0fSWilliam Breathitt Gray 	return 0;
739f1d8a071SWilliam Breathitt Gray }
740f1d8a071SWilliam Breathitt Gray 
741e612b600SWilliam Breathitt Gray static void quad8_preset_register_set(struct quad8 *const priv, const int id,
742e612b600SWilliam Breathitt Gray 				      const unsigned int preset)
743fc069262SSyed Nayyar Waris {
744daae1ee5SWilliam Breathitt Gray 	struct channel_reg __iomem *const chan = priv->reg->channel + id;
745fc069262SSyed Nayyar Waris 	int i;
746fc069262SSyed Nayyar Waris 
747e357e81fSWilliam Breathitt Gray 	priv->preset[id] = preset;
748fc069262SSyed Nayyar Waris 
749fc069262SSyed Nayyar Waris 	/* Reset Byte Pointer */
750daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, &chan->control);
751fc069262SSyed Nayyar Waris 
752fc069262SSyed Nayyar Waris 	/* Set Preset Register */
753fc069262SSyed Nayyar Waris 	for (i = 0; i < 3; i++)
754daae1ee5SWilliam Breathitt Gray 		iowrite8(preset >> (8 * i), &chan->data);
755fc069262SSyed Nayyar Waris }
756fc069262SSyed Nayyar Waris 
757aaec1a0fSWilliam Breathitt Gray static int quad8_count_preset_write(struct counter_device *counter,
758aaec1a0fSWilliam Breathitt Gray 				    struct counter_count *count, u64 preset)
759f1d8a071SWilliam Breathitt Gray {
760aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
76109db4678SWilliam Breathitt Gray 	unsigned long irqflags;
762f1d8a071SWilliam Breathitt Gray 
763f1d8a071SWilliam Breathitt Gray 	/* Only 24-bit values are supported */
764f1d8a071SWilliam Breathitt Gray 	if (preset > 0xFFFFFF)
765e2ff3198SWilliam Breathitt Gray 		return -ERANGE;
766f1d8a071SWilliam Breathitt Gray 
76709db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
768f1d8a071SWilliam Breathitt Gray 
769fc069262SSyed Nayyar Waris 	quad8_preset_register_set(priv, count->id, preset);
770f1d8a071SWilliam Breathitt Gray 
77109db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
772f1d8a071SWilliam Breathitt Gray 
773aaec1a0fSWilliam Breathitt Gray 	return 0;
774f1d8a071SWilliam Breathitt Gray }
775f1d8a071SWilliam Breathitt Gray 
776aaec1a0fSWilliam Breathitt Gray static int quad8_count_ceiling_read(struct counter_device *counter,
777aaec1a0fSWilliam Breathitt Gray 				    struct counter_count *count, u64 *ceiling)
778f1d8a071SWilliam Breathitt Gray {
779aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
78009db4678SWilliam Breathitt Gray 	unsigned long irqflags;
781fc069262SSyed Nayyar Waris 
78209db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
783f1d8a071SWilliam Breathitt Gray 
784f1d8a071SWilliam Breathitt Gray 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
785f1d8a071SWilliam Breathitt Gray 	switch (priv->count_mode[count->id]) {
786f1d8a071SWilliam Breathitt Gray 	case 1:
787f1d8a071SWilliam Breathitt Gray 	case 3:
788aaec1a0fSWilliam Breathitt Gray 		*ceiling = priv->preset[count->id];
789aaec1a0fSWilliam Breathitt Gray 		break;
790aaec1a0fSWilliam Breathitt Gray 	default:
791f1d8a071SWilliam Breathitt Gray 		/* By default 0x1FFFFFF (25 bits unsigned) is maximum count */
792aaec1a0fSWilliam Breathitt Gray 		*ceiling = 0x1FFFFFF;
793aaec1a0fSWilliam Breathitt Gray 		break;
794f1d8a071SWilliam Breathitt Gray 	}
795f1d8a071SWilliam Breathitt Gray 
79609db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
797aaec1a0fSWilliam Breathitt Gray 
798aaec1a0fSWilliam Breathitt Gray 	return 0;
799aaec1a0fSWilliam Breathitt Gray }
800aaec1a0fSWilliam Breathitt Gray 
801aaec1a0fSWilliam Breathitt Gray static int quad8_count_ceiling_write(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 
807fc069262SSyed Nayyar Waris 	/* Only 24-bit values are supported */
808fc069262SSyed Nayyar Waris 	if (ceiling > 0xFFFFFF)
809e2ff3198SWilliam Breathitt Gray 		return -ERANGE;
810fc069262SSyed Nayyar Waris 
81109db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
812f1d8a071SWilliam Breathitt Gray 
813f1d8a071SWilliam Breathitt Gray 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
814f1d8a071SWilliam Breathitt Gray 	switch (priv->count_mode[count->id]) {
815f1d8a071SWilliam Breathitt Gray 	case 1:
816f1d8a071SWilliam Breathitt Gray 	case 3:
817fc069262SSyed Nayyar Waris 		quad8_preset_register_set(priv, count->id, ceiling);
81809db4678SWilliam Breathitt Gray 		spin_unlock_irqrestore(&priv->lock, irqflags);
819aaec1a0fSWilliam Breathitt Gray 		return 0;
820f1d8a071SWilliam Breathitt Gray 	}
821f1d8a071SWilliam Breathitt Gray 
82209db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
823fc069262SSyed Nayyar Waris 
824728246e8SWilliam Breathitt Gray 	return -EINVAL;
825f1d8a071SWilliam Breathitt Gray }
826f1d8a071SWilliam Breathitt Gray 
827aaec1a0fSWilliam Breathitt Gray static int quad8_count_preset_enable_read(struct counter_device *counter,
828aaec1a0fSWilliam Breathitt Gray 					  struct counter_count *count,
829aaec1a0fSWilliam Breathitt Gray 					  u8 *preset_enable)
830f1d8a071SWilliam Breathitt Gray {
831aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
832f1d8a071SWilliam Breathitt Gray 
833aaec1a0fSWilliam Breathitt Gray 	*preset_enable = !priv->preset_enable[count->id];
834aaec1a0fSWilliam Breathitt Gray 
835aaec1a0fSWilliam Breathitt Gray 	return 0;
836f1d8a071SWilliam Breathitt Gray }
837f1d8a071SWilliam Breathitt Gray 
838aaec1a0fSWilliam Breathitt Gray static int quad8_count_preset_enable_write(struct counter_device *counter,
839aaec1a0fSWilliam Breathitt Gray 					   struct counter_count *count,
840aaec1a0fSWilliam Breathitt Gray 					   u8 preset_enable)
841f1d8a071SWilliam Breathitt Gray {
842aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
843daae1ee5SWilliam Breathitt Gray 	u8 __iomem *const control = &priv->reg->channel[count->id].control;
84409db4678SWilliam Breathitt Gray 	unsigned long irqflags;
845f1d8a071SWilliam Breathitt Gray 	unsigned int ior_cfg;
846f1d8a071SWilliam Breathitt Gray 
847f1d8a071SWilliam Breathitt Gray 	/* Preset enable is active low in Input/Output Control register */
848f1d8a071SWilliam Breathitt Gray 	preset_enable = !preset_enable;
849f1d8a071SWilliam Breathitt Gray 
85009db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
851fc069262SSyed Nayyar Waris 
852f1d8a071SWilliam Breathitt Gray 	priv->preset_enable[count->id] = preset_enable;
853f1d8a071SWilliam Breathitt Gray 
8547aa2ba0dSWilliam Breathitt Gray 	ior_cfg = priv->ab_enable[count->id] | preset_enable << 1 |
8557aa2ba0dSWilliam Breathitt Gray 		  priv->irq_trigger[count->id] << 3;
856f1d8a071SWilliam Breathitt Gray 
857f1d8a071SWilliam Breathitt Gray 	/* Load I/O control configuration to Input / Output Control Register */
858daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_IOR | ior_cfg, control);
859f1d8a071SWilliam Breathitt Gray 
86009db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
861fc069262SSyed Nayyar Waris 
862aaec1a0fSWilliam Breathitt Gray 	return 0;
863f1d8a071SWilliam Breathitt Gray }
864f1d8a071SWilliam Breathitt Gray 
865aaec1a0fSWilliam Breathitt Gray static int quad8_signal_cable_fault_read(struct counter_device *counter,
866954ab5ccSWilliam Breathitt Gray 					 struct counter_signal *signal,
867aaec1a0fSWilliam Breathitt Gray 					 u8 *cable_fault)
868954ab5ccSWilliam Breathitt Gray {
869aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
870954ab5ccSWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
87109db4678SWilliam Breathitt Gray 	unsigned long irqflags;
872708d9893SSyed Nayyar Waris 	bool disabled;
873954ab5ccSWilliam Breathitt Gray 	unsigned int status;
874954ab5ccSWilliam Breathitt Gray 
87509db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
876708d9893SSyed Nayyar Waris 
877708d9893SSyed Nayyar Waris 	disabled = !(priv->cable_fault_enable & BIT(channel_id));
878708d9893SSyed Nayyar Waris 
879708d9893SSyed Nayyar Waris 	if (disabled) {
88009db4678SWilliam Breathitt Gray 		spin_unlock_irqrestore(&priv->lock, irqflags);
881954ab5ccSWilliam Breathitt Gray 		return -EINVAL;
882708d9893SSyed Nayyar Waris 	}
883954ab5ccSWilliam Breathitt Gray 
884954ab5ccSWilliam Breathitt Gray 	/* Logic 0 = cable fault */
885daae1ee5SWilliam Breathitt Gray 	status = ioread8(&priv->reg->cable_status);
886954ab5ccSWilliam Breathitt Gray 
88709db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
888708d9893SSyed Nayyar Waris 
889954ab5ccSWilliam Breathitt Gray 	/* Mask respective channel and invert logic */
890aaec1a0fSWilliam Breathitt Gray 	*cable_fault = !(status & BIT(channel_id));
891954ab5ccSWilliam Breathitt Gray 
892aaec1a0fSWilliam Breathitt Gray 	return 0;
893954ab5ccSWilliam Breathitt Gray }
894954ab5ccSWilliam Breathitt Gray 
895aaec1a0fSWilliam Breathitt Gray static int quad8_signal_cable_fault_enable_read(struct counter_device *counter,
896aaec1a0fSWilliam Breathitt Gray 						struct counter_signal *signal,
897aaec1a0fSWilliam Breathitt Gray 						u8 *enable)
898954ab5ccSWilliam Breathitt Gray {
899aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
900954ab5ccSWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
901954ab5ccSWilliam Breathitt Gray 
902aaec1a0fSWilliam Breathitt Gray 	*enable = !!(priv->cable_fault_enable & BIT(channel_id));
903aaec1a0fSWilliam Breathitt Gray 
904aaec1a0fSWilliam Breathitt Gray 	return 0;
905954ab5ccSWilliam Breathitt Gray }
906954ab5ccSWilliam Breathitt Gray 
907aaec1a0fSWilliam Breathitt Gray static int quad8_signal_cable_fault_enable_write(struct counter_device *counter,
908aaec1a0fSWilliam Breathitt Gray 						 struct counter_signal *signal,
909aaec1a0fSWilliam Breathitt Gray 						 u8 enable)
910954ab5ccSWilliam Breathitt Gray {
911aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
912954ab5ccSWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
91309db4678SWilliam Breathitt Gray 	unsigned long irqflags;
914954ab5ccSWilliam Breathitt Gray 	unsigned int cable_fault_enable;
915954ab5ccSWilliam Breathitt Gray 
91609db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
917708d9893SSyed Nayyar Waris 
918954ab5ccSWilliam Breathitt Gray 	if (enable)
919954ab5ccSWilliam Breathitt Gray 		priv->cable_fault_enable |= BIT(channel_id);
920954ab5ccSWilliam Breathitt Gray 	else
921954ab5ccSWilliam Breathitt Gray 		priv->cable_fault_enable &= ~BIT(channel_id);
922954ab5ccSWilliam Breathitt Gray 
923954ab5ccSWilliam Breathitt Gray 	/* Enable is active low in Differential Encoder Cable Status register */
924954ab5ccSWilliam Breathitt Gray 	cable_fault_enable = ~priv->cable_fault_enable;
925954ab5ccSWilliam Breathitt Gray 
926daae1ee5SWilliam Breathitt Gray 	iowrite8(cable_fault_enable, &priv->reg->cable_status);
927954ab5ccSWilliam Breathitt Gray 
92809db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
929708d9893SSyed Nayyar Waris 
930aaec1a0fSWilliam Breathitt Gray 	return 0;
931954ab5ccSWilliam Breathitt Gray }
932954ab5ccSWilliam Breathitt Gray 
933aaec1a0fSWilliam Breathitt Gray static int quad8_signal_fck_prescaler_read(struct counter_device *counter,
934aaec1a0fSWilliam Breathitt Gray 					   struct counter_signal *signal,
935aaec1a0fSWilliam Breathitt Gray 					   u8 *prescaler)
936de65d055SWilliam Breathitt Gray {
937aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
938de65d055SWilliam Breathitt Gray 
939aaec1a0fSWilliam Breathitt Gray 	*prescaler = priv->fck_prescaler[signal->id / 2];
940aaec1a0fSWilliam Breathitt Gray 
941aaec1a0fSWilliam Breathitt Gray 	return 0;
942de65d055SWilliam Breathitt Gray }
943de65d055SWilliam Breathitt Gray 
944aaec1a0fSWilliam Breathitt Gray static int quad8_signal_fck_prescaler_write(struct counter_device *counter,
945aaec1a0fSWilliam Breathitt Gray 					    struct counter_signal *signal,
946aaec1a0fSWilliam Breathitt Gray 					    u8 prescaler)
947de65d055SWilliam Breathitt Gray {
948aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
949de65d055SWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
950daae1ee5SWilliam Breathitt Gray 	struct channel_reg __iomem *const chan = priv->reg->channel + channel_id;
95109db4678SWilliam Breathitt Gray 	unsigned long irqflags;
952de65d055SWilliam Breathitt Gray 
95309db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
954d5ed76adSSyed Nayyar Waris 
955de65d055SWilliam Breathitt Gray 	priv->fck_prescaler[channel_id] = prescaler;
956de65d055SWilliam Breathitt Gray 
957de65d055SWilliam Breathitt Gray 	/* Reset Byte Pointer */
958daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, &chan->control);
959de65d055SWilliam Breathitt Gray 
960de65d055SWilliam Breathitt Gray 	/* Set filter clock factor */
961daae1ee5SWilliam Breathitt Gray 	iowrite8(prescaler, &chan->data);
962b6e9cdedSWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
963daae1ee5SWilliam Breathitt Gray 		 &chan->control);
964de65d055SWilliam Breathitt Gray 
96509db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
966d5ed76adSSyed Nayyar Waris 
967aaec1a0fSWilliam Breathitt Gray 	return 0;
968de65d055SWilliam Breathitt Gray }
969de65d055SWilliam Breathitt Gray 
970aaec1a0fSWilliam Breathitt Gray static struct counter_comp quad8_signal_ext[] = {
971aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_BOOL("cable_fault", quad8_signal_cable_fault_read,
972aaec1a0fSWilliam Breathitt Gray 				 NULL),
973aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_BOOL("cable_fault_enable",
974aaec1a0fSWilliam Breathitt Gray 				 quad8_signal_cable_fault_enable_read,
975aaec1a0fSWilliam Breathitt Gray 				 quad8_signal_cable_fault_enable_write),
976aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_U8("filter_clock_prescaler",
977aaec1a0fSWilliam Breathitt Gray 			       quad8_signal_fck_prescaler_read,
978aaec1a0fSWilliam Breathitt Gray 			       quad8_signal_fck_prescaler_write)
979de65d055SWilliam Breathitt Gray };
980de65d055SWilliam Breathitt Gray 
981aaec1a0fSWilliam Breathitt Gray static DEFINE_COUNTER_ENUM(quad8_index_pol_enum, quad8_index_polarity_modes);
982aaec1a0fSWilliam Breathitt Gray static DEFINE_COUNTER_ENUM(quad8_synch_mode_enum, quad8_synchronous_modes);
983aaec1a0fSWilliam Breathitt Gray 
984aaec1a0fSWilliam Breathitt Gray static struct counter_comp quad8_index_ext[] = {
985aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_ENUM("index_polarity", quad8_index_polarity_get,
986aaec1a0fSWilliam Breathitt Gray 				 quad8_index_polarity_set,
987aaec1a0fSWilliam Breathitt Gray 				 quad8_index_pol_enum),
988aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_ENUM("synchronous_mode", quad8_synchronous_mode_get,
989aaec1a0fSWilliam Breathitt Gray 				 quad8_synchronous_mode_set,
990aaec1a0fSWilliam Breathitt Gray 				 quad8_synch_mode_enum),
991f1d8a071SWilliam Breathitt Gray };
992f1d8a071SWilliam Breathitt Gray 
993f1d8a071SWilliam Breathitt Gray #define QUAD8_QUAD_SIGNAL(_id, _name) {		\
994f1d8a071SWilliam Breathitt Gray 	.id = (_id),				\
995de65d055SWilliam Breathitt Gray 	.name = (_name),			\
996de65d055SWilliam Breathitt Gray 	.ext = quad8_signal_ext,		\
997de65d055SWilliam Breathitt Gray 	.num_ext = ARRAY_SIZE(quad8_signal_ext)	\
998f1d8a071SWilliam Breathitt Gray }
999f1d8a071SWilliam Breathitt Gray 
1000f1d8a071SWilliam Breathitt Gray #define	QUAD8_INDEX_SIGNAL(_id, _name) {	\
1001f1d8a071SWilliam Breathitt Gray 	.id = (_id),				\
1002f1d8a071SWilliam Breathitt Gray 	.name = (_name),			\
1003f1d8a071SWilliam Breathitt Gray 	.ext = quad8_index_ext,			\
1004f1d8a071SWilliam Breathitt Gray 	.num_ext = ARRAY_SIZE(quad8_index_ext)	\
1005f1d8a071SWilliam Breathitt Gray }
1006f1d8a071SWilliam Breathitt Gray 
1007f1d8a071SWilliam Breathitt Gray static struct counter_signal quad8_signals[] = {
1008f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(0, "Channel 1 Quadrature A"),
1009f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(1, "Channel 1 Quadrature B"),
1010f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(2, "Channel 2 Quadrature A"),
1011f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(3, "Channel 2 Quadrature B"),
1012f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(4, "Channel 3 Quadrature A"),
1013f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(5, "Channel 3 Quadrature B"),
1014f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(6, "Channel 4 Quadrature A"),
1015f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(7, "Channel 4 Quadrature B"),
1016f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(8, "Channel 5 Quadrature A"),
1017f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(9, "Channel 5 Quadrature B"),
1018f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(10, "Channel 6 Quadrature A"),
1019f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(11, "Channel 6 Quadrature B"),
1020f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(12, "Channel 7 Quadrature A"),
1021f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(13, "Channel 7 Quadrature B"),
1022f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(14, "Channel 8 Quadrature A"),
1023f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(15, "Channel 8 Quadrature B"),
1024f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(16, "Channel 1 Index"),
1025f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(17, "Channel 2 Index"),
1026f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(18, "Channel 3 Index"),
1027f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(19, "Channel 4 Index"),
1028f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(20, "Channel 5 Index"),
1029f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(21, "Channel 6 Index"),
1030f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(22, "Channel 7 Index"),
1031f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(23, "Channel 8 Index")
1032f1d8a071SWilliam Breathitt Gray };
1033f1d8a071SWilliam Breathitt Gray 
1034f1d8a071SWilliam Breathitt Gray #define QUAD8_COUNT_SYNAPSES(_id) {					\
1035f1d8a071SWilliam Breathitt Gray 	{								\
1036f1d8a071SWilliam Breathitt Gray 		.actions_list = quad8_synapse_actions_list,		\
1037f1d8a071SWilliam Breathitt Gray 		.num_actions = ARRAY_SIZE(quad8_synapse_actions_list),	\
1038f1d8a071SWilliam Breathitt Gray 		.signal = quad8_signals + 2 * (_id)			\
1039f1d8a071SWilliam Breathitt Gray 	},								\
1040f1d8a071SWilliam Breathitt Gray 	{								\
1041f1d8a071SWilliam Breathitt Gray 		.actions_list = quad8_synapse_actions_list,		\
1042f1d8a071SWilliam Breathitt Gray 		.num_actions = ARRAY_SIZE(quad8_synapse_actions_list),	\
1043f1d8a071SWilliam Breathitt Gray 		.signal = quad8_signals + 2 * (_id) + 1			\
1044f1d8a071SWilliam Breathitt Gray 	},								\
1045f1d8a071SWilliam Breathitt Gray 	{								\
1046f1d8a071SWilliam Breathitt Gray 		.actions_list = quad8_index_actions_list,		\
1047f1d8a071SWilliam Breathitt Gray 		.num_actions = ARRAY_SIZE(quad8_index_actions_list),	\
1048f1d8a071SWilliam Breathitt Gray 		.signal = quad8_signals + 2 * (_id) + 16		\
1049f1d8a071SWilliam Breathitt Gray 	}								\
1050f1d8a071SWilliam Breathitt Gray }
1051f1d8a071SWilliam Breathitt Gray 
1052f1d8a071SWilliam Breathitt Gray static struct counter_synapse quad8_count_synapses[][3] = {
1053f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(0), QUAD8_COUNT_SYNAPSES(1),
1054f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(2), QUAD8_COUNT_SYNAPSES(3),
1055f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(4), QUAD8_COUNT_SYNAPSES(5),
1056f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(6), QUAD8_COUNT_SYNAPSES(7)
1057f1d8a071SWilliam Breathitt Gray };
1058f1d8a071SWilliam Breathitt Gray 
1059aaec1a0fSWilliam Breathitt Gray static const enum counter_count_mode quad8_cnt_modes[] = {
1060aaec1a0fSWilliam Breathitt Gray 	COUNTER_COUNT_MODE_NORMAL,
1061aaec1a0fSWilliam Breathitt Gray 	COUNTER_COUNT_MODE_RANGE_LIMIT,
1062aaec1a0fSWilliam Breathitt Gray 	COUNTER_COUNT_MODE_NON_RECYCLE,
1063aaec1a0fSWilliam Breathitt Gray 	COUNTER_COUNT_MODE_MODULO_N,
1064aaec1a0fSWilliam Breathitt Gray };
1065aaec1a0fSWilliam Breathitt Gray 
1066aaec1a0fSWilliam Breathitt Gray static DEFINE_COUNTER_AVAILABLE(quad8_count_mode_available, quad8_cnt_modes);
1067aaec1a0fSWilliam Breathitt Gray 
1068aaec1a0fSWilliam Breathitt Gray static DEFINE_COUNTER_ENUM(quad8_error_noise_enum, quad8_noise_error_states);
1069aaec1a0fSWilliam Breathitt Gray 
1070aaec1a0fSWilliam Breathitt Gray static struct counter_comp quad8_count_ext[] = {
1071aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_CEILING(quad8_count_ceiling_read,
1072aaec1a0fSWilliam Breathitt Gray 			     quad8_count_ceiling_write),
1073aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_FLOOR(quad8_count_floor_read, NULL),
1074aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_COUNT_MODE(quad8_count_mode_read, quad8_count_mode_write,
1075aaec1a0fSWilliam Breathitt Gray 				quad8_count_mode_available),
1076aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_DIRECTION(quad8_direction_read),
1077aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_ENABLE(quad8_count_enable_read, quad8_count_enable_write),
1078aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_COUNT_ENUM("error_noise", quad8_error_noise_get, NULL,
1079aaec1a0fSWilliam Breathitt Gray 				quad8_error_noise_enum),
1080aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_PRESET(quad8_count_preset_read, quad8_count_preset_write),
1081aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_PRESET_ENABLE(quad8_count_preset_enable_read,
1082aaec1a0fSWilliam Breathitt Gray 				   quad8_count_preset_enable_write),
1083f1d8a071SWilliam Breathitt Gray };
1084f1d8a071SWilliam Breathitt Gray 
1085f1d8a071SWilliam Breathitt Gray #define QUAD8_COUNT(_id, _cntname) {					\
1086f1d8a071SWilliam Breathitt Gray 	.id = (_id),							\
1087f1d8a071SWilliam Breathitt Gray 	.name = (_cntname),						\
1088f1d8a071SWilliam Breathitt Gray 	.functions_list = quad8_count_functions_list,			\
1089f1d8a071SWilliam Breathitt Gray 	.num_functions = ARRAY_SIZE(quad8_count_functions_list),	\
1090f1d8a071SWilliam Breathitt Gray 	.synapses = quad8_count_synapses[(_id)],			\
1091f1d8a071SWilliam Breathitt Gray 	.num_synapses =	2,						\
1092f1d8a071SWilliam Breathitt Gray 	.ext = quad8_count_ext,						\
1093f1d8a071SWilliam Breathitt Gray 	.num_ext = ARRAY_SIZE(quad8_count_ext)				\
1094f1d8a071SWilliam Breathitt Gray }
1095f1d8a071SWilliam Breathitt Gray 
1096f1d8a071SWilliam Breathitt Gray static struct counter_count quad8_counts[] = {
1097f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(0, "Channel 1 Count"),
1098f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(1, "Channel 2 Count"),
1099f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(2, "Channel 3 Count"),
1100f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(3, "Channel 4 Count"),
1101f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(4, "Channel 5 Count"),
1102f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(5, "Channel 6 Count"),
1103f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(6, "Channel 7 Count"),
1104f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(7, "Channel 8 Count")
1105f1d8a071SWilliam Breathitt Gray };
1106f1d8a071SWilliam Breathitt Gray 
11077aa2ba0dSWilliam Breathitt Gray static irqreturn_t quad8_irq_handler(int irq, void *private)
11087aa2ba0dSWilliam Breathitt Gray {
11099e884bb1SUwe Kleine-König 	struct counter_device *counter = private;
11109e884bb1SUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
11117aa2ba0dSWilliam Breathitt Gray 	unsigned long irq_status;
11127aa2ba0dSWilliam Breathitt Gray 	unsigned long channel;
11137aa2ba0dSWilliam Breathitt Gray 	u8 event;
11147aa2ba0dSWilliam Breathitt Gray 
1115daae1ee5SWilliam Breathitt Gray 	irq_status = ioread8(&priv->reg->interrupt_status);
11167aa2ba0dSWilliam Breathitt Gray 	if (!irq_status)
11177aa2ba0dSWilliam Breathitt Gray 		return IRQ_NONE;
11187aa2ba0dSWilliam Breathitt Gray 
11197aa2ba0dSWilliam Breathitt Gray 	for_each_set_bit(channel, &irq_status, QUAD8_NUM_COUNTERS) {
11207aa2ba0dSWilliam Breathitt Gray 		switch (priv->irq_trigger[channel]) {
11217aa2ba0dSWilliam Breathitt Gray 		case QUAD8_EVENT_CARRY:
11227aa2ba0dSWilliam Breathitt Gray 			event = COUNTER_EVENT_OVERFLOW;
11237aa2ba0dSWilliam Breathitt Gray 				break;
11247aa2ba0dSWilliam Breathitt Gray 		case QUAD8_EVENT_COMPARE:
11257aa2ba0dSWilliam Breathitt Gray 			event = COUNTER_EVENT_THRESHOLD;
11267aa2ba0dSWilliam Breathitt Gray 				break;
11277aa2ba0dSWilliam Breathitt Gray 		case QUAD8_EVENT_CARRY_BORROW:
11287aa2ba0dSWilliam Breathitt Gray 			event = COUNTER_EVENT_OVERFLOW_UNDERFLOW;
11297aa2ba0dSWilliam Breathitt Gray 				break;
11307aa2ba0dSWilliam Breathitt Gray 		case QUAD8_EVENT_INDEX:
11317aa2ba0dSWilliam Breathitt Gray 			event = COUNTER_EVENT_INDEX;
11327aa2ba0dSWilliam Breathitt Gray 				break;
11337aa2ba0dSWilliam Breathitt Gray 		default:
11347aa2ba0dSWilliam Breathitt Gray 			/* should never reach this path */
11357aa2ba0dSWilliam Breathitt Gray 			WARN_ONCE(true, "invalid interrupt trigger function %u configured for channel %lu\n",
11367aa2ba0dSWilliam Breathitt Gray 				  priv->irq_trigger[channel], channel);
11377aa2ba0dSWilliam Breathitt Gray 			continue;
11387aa2ba0dSWilliam Breathitt Gray 		}
11397aa2ba0dSWilliam Breathitt Gray 
11409e884bb1SUwe Kleine-König 		counter_push_event(counter, event, channel);
11417aa2ba0dSWilliam Breathitt Gray 	}
11427aa2ba0dSWilliam Breathitt Gray 
11437aa2ba0dSWilliam Breathitt Gray 	/* Clear pending interrupts on device */
1144daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, &priv->reg->channel_oper);
11457aa2ba0dSWilliam Breathitt Gray 
11467aa2ba0dSWilliam Breathitt Gray 	return IRQ_HANDLED;
11477aa2ba0dSWilliam Breathitt Gray }
11487aa2ba0dSWilliam Breathitt Gray 
1149daae1ee5SWilliam Breathitt Gray static void quad8_init_counter(struct channel_reg __iomem *const chan)
1150b6e9cdedSWilliam Breathitt Gray {
1151b6e9cdedSWilliam Breathitt Gray 	unsigned long i;
1152b6e9cdedSWilliam Breathitt Gray 
1153b6e9cdedSWilliam Breathitt Gray 	/* Reset Byte Pointer */
1154daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, &chan->control);
1155b6e9cdedSWilliam Breathitt Gray 	/* Reset filter clock factor */
1156daae1ee5SWilliam Breathitt Gray 	iowrite8(0, &chan->data);
1157b6e9cdedSWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
1158daae1ee5SWilliam Breathitt Gray 		 &chan->control);
1159b6e9cdedSWilliam Breathitt Gray 	/* Reset Byte Pointer */
1160daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, &chan->control);
1161b6e9cdedSWilliam Breathitt Gray 	/* Reset Preset Register */
1162b6e9cdedSWilliam Breathitt Gray 	for (i = 0; i < 3; i++)
1163daae1ee5SWilliam Breathitt Gray 		iowrite8(0x00, &chan->data);
1164b6e9cdedSWilliam Breathitt Gray 	/* Reset Borrow, Carry, Compare, and Sign flags */
1165daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, &chan->control);
1166b6e9cdedSWilliam Breathitt Gray 	/* Reset Error flag */
1167daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, &chan->control);
1168b6e9cdedSWilliam Breathitt Gray 	/* Binary encoding; Normal count; non-quadrature mode */
1169daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_CMR, &chan->control);
1170b6e9cdedSWilliam Breathitt Gray 	/* Disable A and B inputs; preset on index; FLG1 as Carry */
1171daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_IOR, &chan->control);
1172b6e9cdedSWilliam Breathitt Gray 	/* Disable index function; negative index polarity */
1173daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CTR_IDR, &chan->control);
1174b6e9cdedSWilliam Breathitt Gray }
1175b6e9cdedSWilliam Breathitt Gray 
1176f1d8a071SWilliam Breathitt Gray static int quad8_probe(struct device *dev, unsigned int id)
1177f1d8a071SWilliam Breathitt Gray {
11789e884bb1SUwe Kleine-König 	struct counter_device *counter;
1179e357e81fSWilliam Breathitt Gray 	struct quad8 *priv;
1180b6e9cdedSWilliam Breathitt Gray 	unsigned long i;
11817aa2ba0dSWilliam Breathitt Gray 	int err;
1182f1d8a071SWilliam Breathitt Gray 
1183f1d8a071SWilliam Breathitt Gray 	if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) {
1184f1d8a071SWilliam Breathitt Gray 		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
1185f1d8a071SWilliam Breathitt Gray 			base[id], base[id] + QUAD8_EXTENT);
1186f1d8a071SWilliam Breathitt Gray 		return -EBUSY;
1187f1d8a071SWilliam Breathitt Gray 	}
1188f1d8a071SWilliam Breathitt Gray 
11899e884bb1SUwe Kleine-König 	counter = devm_counter_alloc(dev, sizeof(*priv));
11909e884bb1SUwe Kleine-König 	if (!counter)
1191f1d8a071SWilliam Breathitt Gray 		return -ENOMEM;
11929e884bb1SUwe Kleine-König 	priv = counter_priv(counter);
1193f1d8a071SWilliam Breathitt Gray 
1194daae1ee5SWilliam Breathitt Gray 	priv->reg = devm_ioport_map(dev, base[id], QUAD8_EXTENT);
1195daae1ee5SWilliam Breathitt Gray 	if (!priv->reg)
1196b6e9cdedSWilliam Breathitt Gray 		return -ENOMEM;
1197b6e9cdedSWilliam Breathitt Gray 
1198f1d8a071SWilliam Breathitt Gray 	/* Initialize Counter device and driver data */
11999e884bb1SUwe Kleine-König 	counter->name = dev_name(dev);
12009e884bb1SUwe Kleine-König 	counter->parent = dev;
12019e884bb1SUwe Kleine-König 	counter->ops = &quad8_ops;
12029e884bb1SUwe Kleine-König 	counter->counts = quad8_counts;
12039e884bb1SUwe Kleine-König 	counter->num_counts = ARRAY_SIZE(quad8_counts);
12049e884bb1SUwe Kleine-König 	counter->signals = quad8_signals;
12059e884bb1SUwe Kleine-König 	counter->num_signals = ARRAY_SIZE(quad8_signals);
1206f1d8a071SWilliam Breathitt Gray 
120709db4678SWilliam Breathitt Gray 	spin_lock_init(&priv->lock);
1208fc069262SSyed Nayyar Waris 
12097aa2ba0dSWilliam Breathitt Gray 	/* Reset Index/Interrupt Register */
1210daae1ee5SWilliam Breathitt Gray 	iowrite8(0x00, &priv->reg->index_interrupt);
1211f1d8a071SWilliam Breathitt Gray 	/* Reset all counters and disable interrupt function */
1212daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CHAN_OP_RESET_COUNTERS, &priv->reg->channel_oper);
1213f1d8a071SWilliam Breathitt Gray 	/* Set initial configuration for all counters */
1214b6e9cdedSWilliam Breathitt Gray 	for (i = 0; i < QUAD8_NUM_COUNTERS; i++)
1215daae1ee5SWilliam Breathitt Gray 		quad8_init_counter(priv->reg->channel + i);
1216954ab5ccSWilliam Breathitt Gray 	/* Disable Differential Encoder Cable Status for all channels */
1217daae1ee5SWilliam Breathitt Gray 	iowrite8(0xFF, &priv->reg->cable_status);
12187aa2ba0dSWilliam Breathitt Gray 	/* Enable all counters and enable interrupt function */
1219daae1ee5SWilliam Breathitt Gray 	iowrite8(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, &priv->reg->channel_oper);
12207aa2ba0dSWilliam Breathitt Gray 
1221663d8fb0SWilliam Breathitt Gray 	err = devm_request_irq(&counter->dev, irq[id], quad8_irq_handler,
1222663d8fb0SWilliam Breathitt Gray 			       IRQF_SHARED, counter->name, counter);
12237aa2ba0dSWilliam Breathitt Gray 	if (err)
12247aa2ba0dSWilliam Breathitt Gray 		return err;
1225f1d8a071SWilliam Breathitt Gray 
12269e884bb1SUwe Kleine-König 	err = devm_counter_add(dev, counter);
12279e884bb1SUwe Kleine-König 	if (err < 0)
12289e884bb1SUwe Kleine-König 		return dev_err_probe(dev, err, "Failed to add counter\n");
12299e884bb1SUwe Kleine-König 
12309e884bb1SUwe Kleine-König 	return 0;
1231f1d8a071SWilliam Breathitt Gray }
1232f1d8a071SWilliam Breathitt Gray 
1233f1d8a071SWilliam Breathitt Gray static struct isa_driver quad8_driver = {
1234f1d8a071SWilliam Breathitt Gray 	.probe = quad8_probe,
1235f1d8a071SWilliam Breathitt Gray 	.driver = {
1236f1d8a071SWilliam Breathitt Gray 		.name = "104-quad-8"
1237f1d8a071SWilliam Breathitt Gray 	}
1238f1d8a071SWilliam Breathitt Gray };
1239f1d8a071SWilliam Breathitt Gray 
1240*0c83a280SWilliam Breathitt Gray module_isa_driver_with_irq(quad8_driver, num_quad8, num_irq);
1241f1d8a071SWilliam Breathitt Gray 
1242f1d8a071SWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
1243e357e81fSWilliam Breathitt Gray MODULE_DESCRIPTION("ACCES 104-QUAD-8 driver");
1244f1d8a071SWilliam Breathitt Gray MODULE_LICENSE("GPL v2");
1245