xref: /openbmc/linux/drivers/counter/104-quad-8.c (revision aea8334b)
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 /**
37e357e81fSWilliam Breathitt Gray  * struct quad8 - device private data structure
3894a853ecSWilliam Breathitt Gray  * @lock:		lock to prevent clobbering device states during R/W ops
39f1d8a071SWilliam Breathitt Gray  * @counter:		instance of the counter_device
40954ab5ccSWilliam Breathitt Gray  * @fck_prescaler:	array of filter clock prescaler configurations
41f1d8a071SWilliam Breathitt Gray  * @preset:		array of preset values
42f1d8a071SWilliam Breathitt Gray  * @count_mode:		array of count mode configurations
43f1d8a071SWilliam Breathitt Gray  * @quadrature_mode:	array of quadrature mode configurations
44f1d8a071SWilliam Breathitt Gray  * @quadrature_scale:	array of quadrature mode scale configurations
45f1d8a071SWilliam Breathitt Gray  * @ab_enable:		array of A and B inputs enable configurations
46f1d8a071SWilliam Breathitt Gray  * @preset_enable:	array of set_to_preset_on_index attribute configurations
477aa2ba0dSWilliam Breathitt Gray  * @irq_trigger:	array of current IRQ trigger function configurations
48f1d8a071SWilliam Breathitt Gray  * @synchronous_mode:	array of index function synchronous mode configurations
49f1d8a071SWilliam Breathitt Gray  * @index_polarity:	array of index function polarity configurations
50954ab5ccSWilliam Breathitt Gray  * @cable_fault_enable:	differential encoder cable status enable configurations
51e357e81fSWilliam Breathitt Gray  * @base:		base port address of the device
52f1d8a071SWilliam Breathitt Gray  */
53e357e81fSWilliam Breathitt Gray struct quad8 {
5409db4678SWilliam Breathitt Gray 	spinlock_t lock;
55f1d8a071SWilliam Breathitt Gray 	struct counter_device counter;
56de65d055SWilliam Breathitt Gray 	unsigned int fck_prescaler[QUAD8_NUM_COUNTERS];
57f1d8a071SWilliam Breathitt Gray 	unsigned int preset[QUAD8_NUM_COUNTERS];
58f1d8a071SWilliam Breathitt Gray 	unsigned int count_mode[QUAD8_NUM_COUNTERS];
59f1d8a071SWilliam Breathitt Gray 	unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
60f1d8a071SWilliam Breathitt Gray 	unsigned int quadrature_scale[QUAD8_NUM_COUNTERS];
61f1d8a071SWilliam Breathitt Gray 	unsigned int ab_enable[QUAD8_NUM_COUNTERS];
62f1d8a071SWilliam Breathitt Gray 	unsigned int preset_enable[QUAD8_NUM_COUNTERS];
637aa2ba0dSWilliam Breathitt Gray 	unsigned int irq_trigger[QUAD8_NUM_COUNTERS];
64f1d8a071SWilliam Breathitt Gray 	unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
65f1d8a071SWilliam Breathitt Gray 	unsigned int index_polarity[QUAD8_NUM_COUNTERS];
66954ab5ccSWilliam Breathitt Gray 	unsigned int cable_fault_enable;
67f1d8a071SWilliam Breathitt Gray 	unsigned int base;
68f1d8a071SWilliam Breathitt Gray };
69f1d8a071SWilliam Breathitt Gray 
707aa2ba0dSWilliam Breathitt Gray #define QUAD8_REG_INTERRUPT_STATUS 0x10
71f1d8a071SWilliam Breathitt Gray #define QUAD8_REG_CHAN_OP 0x11
727aa2ba0dSWilliam Breathitt Gray #define QUAD8_REG_INDEX_INTERRUPT 0x12
73f1d8a071SWilliam Breathitt Gray #define QUAD8_REG_INDEX_INPUT_LEVELS 0x16
74954ab5ccSWilliam Breathitt Gray #define QUAD8_DIFF_ENCODER_CABLE_STATUS 0x17
75f1d8a071SWilliam Breathitt Gray /* Borrow Toggle flip-flop */
76f1d8a071SWilliam Breathitt Gray #define QUAD8_FLAG_BT BIT(0)
77f1d8a071SWilliam Breathitt Gray /* Carry Toggle flip-flop */
78f1d8a071SWilliam Breathitt Gray #define QUAD8_FLAG_CT BIT(1)
79f1d8a071SWilliam Breathitt Gray /* Error flag */
80f1d8a071SWilliam Breathitt Gray #define QUAD8_FLAG_E BIT(4)
81f1d8a071SWilliam Breathitt Gray /* Up/Down flag */
82f1d8a071SWilliam Breathitt Gray #define QUAD8_FLAG_UD BIT(5)
83f1d8a071SWilliam Breathitt Gray /* Reset and Load Signal Decoders */
84f1d8a071SWilliam Breathitt Gray #define QUAD8_CTR_RLD 0x00
85f1d8a071SWilliam Breathitt Gray /* Counter Mode Register */
86f1d8a071SWilliam Breathitt Gray #define QUAD8_CTR_CMR 0x20
87f1d8a071SWilliam Breathitt Gray /* Input / Output Control Register */
88f1d8a071SWilliam Breathitt Gray #define QUAD8_CTR_IOR 0x40
89f1d8a071SWilliam Breathitt Gray /* Index Control Register */
90f1d8a071SWilliam Breathitt Gray #define QUAD8_CTR_IDR 0x60
91f1d8a071SWilliam Breathitt Gray /* Reset Byte Pointer (three byte data pointer) */
92f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_RESET_BP 0x01
93f1d8a071SWilliam Breathitt Gray /* Reset Counter */
94f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_RESET_CNTR 0x02
95f1d8a071SWilliam Breathitt Gray /* Reset Borrow Toggle, Carry Toggle, Compare Toggle, and Sign flags */
96f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_RESET_FLAGS 0x04
97f1d8a071SWilliam Breathitt Gray /* Reset Error flag */
98f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_RESET_E 0x06
99f1d8a071SWilliam Breathitt Gray /* Preset Register to Counter */
100f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_PRESET_CNTR 0x08
101f1d8a071SWilliam Breathitt Gray /* Transfer Counter to Output Latch */
102f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_CNTR_OUT 0x10
103de65d055SWilliam Breathitt Gray /* Transfer Preset Register LSB to FCK Prescaler */
104de65d055SWilliam Breathitt Gray #define QUAD8_RLD_PRESET_PSC 0x18
105f1d8a071SWilliam Breathitt Gray #define QUAD8_CHAN_OP_RESET_COUNTERS 0x01
1067aa2ba0dSWilliam Breathitt Gray #define QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC 0x04
107f1d8a071SWilliam Breathitt Gray #define QUAD8_CMR_QUADRATURE_X1 0x08
108f1d8a071SWilliam Breathitt Gray #define QUAD8_CMR_QUADRATURE_X2 0x10
109f1d8a071SWilliam Breathitt Gray #define QUAD8_CMR_QUADRATURE_X4 0x18
110f1d8a071SWilliam Breathitt Gray 
111f1d8a071SWilliam Breathitt Gray static int quad8_signal_read(struct counter_device *counter,
112493b938aSWilliam Breathitt Gray 			     struct counter_signal *signal,
113493b938aSWilliam Breathitt Gray 			     enum counter_signal_level *level)
114f1d8a071SWilliam Breathitt Gray {
115*aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
116f1d8a071SWilliam Breathitt Gray 	unsigned int state;
117f1d8a071SWilliam Breathitt Gray 
118f1d8a071SWilliam Breathitt Gray 	/* Only Index signal levels can be read */
119f1d8a071SWilliam Breathitt Gray 	if (signal->id < 16)
120f1d8a071SWilliam Breathitt Gray 		return -EINVAL;
121f1d8a071SWilliam Breathitt Gray 
122f1d8a071SWilliam Breathitt Gray 	state = inb(priv->base + QUAD8_REG_INDEX_INPUT_LEVELS)
123f1d8a071SWilliam Breathitt Gray 		& BIT(signal->id - 16);
124f1d8a071SWilliam Breathitt Gray 
125493b938aSWilliam Breathitt Gray 	*level = (state) ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW;
126f1d8a071SWilliam Breathitt Gray 
127f1d8a071SWilliam Breathitt Gray 	return 0;
128f1d8a071SWilliam Breathitt Gray }
129f1d8a071SWilliam Breathitt Gray 
130f1d8a071SWilliam Breathitt Gray static int quad8_count_read(struct counter_device *counter,
131aaec1a0fSWilliam Breathitt Gray 			    struct counter_count *count, u64 *val)
132f1d8a071SWilliam Breathitt Gray {
133*aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
134f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id;
135f1d8a071SWilliam Breathitt Gray 	unsigned int flags;
136f1d8a071SWilliam Breathitt Gray 	unsigned int borrow;
137f1d8a071SWilliam Breathitt Gray 	unsigned int carry;
13809db4678SWilliam Breathitt Gray 	unsigned long irqflags;
139f1d8a071SWilliam Breathitt Gray 	int i;
140f1d8a071SWilliam Breathitt Gray 
141f1d8a071SWilliam Breathitt Gray 	flags = inb(base_offset + 1);
142f1d8a071SWilliam Breathitt Gray 	borrow = flags & QUAD8_FLAG_BT;
143f1d8a071SWilliam Breathitt Gray 	carry = !!(flags & QUAD8_FLAG_CT);
144f1d8a071SWilliam Breathitt Gray 
145f1d8a071SWilliam Breathitt Gray 	/* Borrow XOR Carry effectively doubles count range */
146d49e6ee2SWilliam Breathitt Gray 	*val = (unsigned long)(borrow ^ carry) << 24;
147f1d8a071SWilliam Breathitt Gray 
14809db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
149fc069262SSyed Nayyar Waris 
150f1d8a071SWilliam Breathitt Gray 	/* Reset Byte Pointer; transfer Counter to Output Latch */
151f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
152f1d8a071SWilliam Breathitt Gray 	     base_offset + 1);
153f1d8a071SWilliam Breathitt Gray 
154f1d8a071SWilliam Breathitt Gray 	for (i = 0; i < 3; i++)
155d49e6ee2SWilliam Breathitt Gray 		*val |= (unsigned long)inb(base_offset) << (8 * i);
156f1d8a071SWilliam Breathitt Gray 
15709db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
158fc069262SSyed Nayyar Waris 
159f1d8a071SWilliam Breathitt Gray 	return 0;
160f1d8a071SWilliam Breathitt Gray }
161f1d8a071SWilliam Breathitt Gray 
162f1d8a071SWilliam Breathitt Gray static int quad8_count_write(struct counter_device *counter,
163aaec1a0fSWilliam Breathitt Gray 			     struct counter_count *count, u64 val)
164f1d8a071SWilliam Breathitt Gray {
165*aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
166f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id;
16709db4678SWilliam Breathitt Gray 	unsigned long irqflags;
168f1d8a071SWilliam Breathitt Gray 	int i;
169f1d8a071SWilliam Breathitt Gray 
170f1d8a071SWilliam Breathitt Gray 	/* Only 24-bit values are supported */
171d49e6ee2SWilliam Breathitt Gray 	if (val > 0xFFFFFF)
172e2ff3198SWilliam Breathitt Gray 		return -ERANGE;
173f1d8a071SWilliam Breathitt Gray 
17409db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
175fc069262SSyed Nayyar Waris 
176f1d8a071SWilliam Breathitt Gray 	/* Reset Byte Pointer */
177f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
178f1d8a071SWilliam Breathitt Gray 
179f1d8a071SWilliam Breathitt Gray 	/* Counter can only be set via Preset Register */
180f1d8a071SWilliam Breathitt Gray 	for (i = 0; i < 3; i++)
181d49e6ee2SWilliam Breathitt Gray 		outb(val >> (8 * i), base_offset);
182f1d8a071SWilliam Breathitt Gray 
183f1d8a071SWilliam Breathitt Gray 	/* Transfer Preset Register to Counter */
184f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_PRESET_CNTR, base_offset + 1);
185f1d8a071SWilliam Breathitt Gray 
186f1d8a071SWilliam Breathitt Gray 	/* Reset Byte Pointer */
187f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
188f1d8a071SWilliam Breathitt Gray 
189f1d8a071SWilliam Breathitt Gray 	/* Set Preset Register back to original value */
190d49e6ee2SWilliam Breathitt Gray 	val = priv->preset[count->id];
191f1d8a071SWilliam Breathitt Gray 	for (i = 0; i < 3; i++)
192d49e6ee2SWilliam Breathitt Gray 		outb(val >> (8 * i), base_offset);
193f1d8a071SWilliam Breathitt Gray 
194f1d8a071SWilliam Breathitt Gray 	/* Reset Borrow, Carry, Compare, and Sign flags */
195f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
196f1d8a071SWilliam Breathitt Gray 	/* Reset Error flag */
197f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
198f1d8a071SWilliam Breathitt Gray 
19909db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
200fc069262SSyed Nayyar Waris 
201f1d8a071SWilliam Breathitt Gray 	return 0;
202f1d8a071SWilliam Breathitt Gray }
203f1d8a071SWilliam Breathitt Gray 
204394a0150SWilliam Breathitt Gray static const enum counter_function quad8_count_functions_list[] = {
205aaec1a0fSWilliam Breathitt Gray 	COUNTER_FUNCTION_PULSE_DIRECTION,
206aaec1a0fSWilliam Breathitt Gray 	COUNTER_FUNCTION_QUADRATURE_X1_A,
207aaec1a0fSWilliam Breathitt Gray 	COUNTER_FUNCTION_QUADRATURE_X2_A,
208aaec1a0fSWilliam Breathitt Gray 	COUNTER_FUNCTION_QUADRATURE_X4,
209f1d8a071SWilliam Breathitt Gray };
210f1d8a071SWilliam Breathitt Gray 
211aaec1a0fSWilliam Breathitt Gray static int quad8_function_read(struct counter_device *counter,
212aaec1a0fSWilliam Breathitt Gray 			       struct counter_count *count,
213aaec1a0fSWilliam Breathitt Gray 			       enum counter_function *function)
214f1d8a071SWilliam Breathitt Gray {
215*aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
216f1d8a071SWilliam Breathitt Gray 	const int id = count->id;
21709db4678SWilliam Breathitt Gray 	unsigned long irqflags;
218f1d8a071SWilliam Breathitt Gray 
21909db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
220fc069262SSyed Nayyar Waris 
221fc069262SSyed Nayyar Waris 	if (priv->quadrature_mode[id])
222fc069262SSyed Nayyar Waris 		switch (priv->quadrature_scale[id]) {
223f1d8a071SWilliam Breathitt Gray 		case 0:
224aaec1a0fSWilliam Breathitt Gray 			*function = COUNTER_FUNCTION_QUADRATURE_X1_A;
225f1d8a071SWilliam Breathitt Gray 			break;
226f1d8a071SWilliam Breathitt Gray 		case 1:
227aaec1a0fSWilliam Breathitt Gray 			*function = COUNTER_FUNCTION_QUADRATURE_X2_A;
228f1d8a071SWilliam Breathitt Gray 			break;
229f1d8a071SWilliam Breathitt Gray 		case 2:
230aaec1a0fSWilliam Breathitt Gray 			*function = COUNTER_FUNCTION_QUADRATURE_X4;
231f1d8a071SWilliam Breathitt Gray 			break;
232f1d8a071SWilliam Breathitt Gray 		}
233f1d8a071SWilliam Breathitt Gray 	else
234aaec1a0fSWilliam Breathitt Gray 		*function = COUNTER_FUNCTION_PULSE_DIRECTION;
235f1d8a071SWilliam Breathitt Gray 
23609db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
237fc069262SSyed Nayyar Waris 
238f1d8a071SWilliam Breathitt Gray 	return 0;
239f1d8a071SWilliam Breathitt Gray }
240f1d8a071SWilliam Breathitt Gray 
241aaec1a0fSWilliam Breathitt Gray static int quad8_function_write(struct counter_device *counter,
242aaec1a0fSWilliam Breathitt Gray 				struct counter_count *count,
243aaec1a0fSWilliam Breathitt Gray 				enum counter_function function)
244f1d8a071SWilliam Breathitt Gray {
245*aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
246f1d8a071SWilliam Breathitt Gray 	const int id = count->id;
247f1d8a071SWilliam Breathitt Gray 	unsigned int *const quadrature_mode = priv->quadrature_mode + id;
248f1d8a071SWilliam Breathitt Gray 	unsigned int *const scale = priv->quadrature_scale + id;
249f1d8a071SWilliam Breathitt Gray 	unsigned int *const synchronous_mode = priv->synchronous_mode + id;
250f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * id + 1;
25109db4678SWilliam Breathitt Gray 	unsigned long irqflags;
252fc069262SSyed Nayyar Waris 	unsigned int mode_cfg;
253fc069262SSyed Nayyar Waris 	unsigned int idr_cfg;
254fc069262SSyed Nayyar Waris 
25509db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
256fc069262SSyed Nayyar Waris 
257fc069262SSyed Nayyar Waris 	mode_cfg = priv->count_mode[id] << 1;
258fc069262SSyed Nayyar Waris 	idr_cfg = priv->index_polarity[id] << 1;
259f1d8a071SWilliam Breathitt Gray 
260aaec1a0fSWilliam Breathitt Gray 	if (function == COUNTER_FUNCTION_PULSE_DIRECTION) {
261f1d8a071SWilliam Breathitt Gray 		*quadrature_mode = 0;
262f1d8a071SWilliam Breathitt Gray 
263f1d8a071SWilliam Breathitt Gray 		/* Quadrature scaling only available in quadrature mode */
264f1d8a071SWilliam Breathitt Gray 		*scale = 0;
265f1d8a071SWilliam Breathitt Gray 
266f1d8a071SWilliam Breathitt Gray 		/* Synchronous function not supported in non-quadrature mode */
267f1d8a071SWilliam Breathitt Gray 		if (*synchronous_mode) {
268f1d8a071SWilliam Breathitt Gray 			*synchronous_mode = 0;
269f1d8a071SWilliam Breathitt Gray 			/* Disable synchronous function mode */
270f1d8a071SWilliam Breathitt Gray 			outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
271f1d8a071SWilliam Breathitt Gray 		}
272f1d8a071SWilliam Breathitt Gray 	} else {
273f1d8a071SWilliam Breathitt Gray 		*quadrature_mode = 1;
274f1d8a071SWilliam Breathitt Gray 
275f1d8a071SWilliam Breathitt Gray 		switch (function) {
276aaec1a0fSWilliam Breathitt Gray 		case COUNTER_FUNCTION_QUADRATURE_X1_A:
277f1d8a071SWilliam Breathitt Gray 			*scale = 0;
278f1d8a071SWilliam Breathitt Gray 			mode_cfg |= QUAD8_CMR_QUADRATURE_X1;
279f1d8a071SWilliam Breathitt Gray 			break;
280aaec1a0fSWilliam Breathitt Gray 		case COUNTER_FUNCTION_QUADRATURE_X2_A:
281f1d8a071SWilliam Breathitt Gray 			*scale = 1;
282f1d8a071SWilliam Breathitt Gray 			mode_cfg |= QUAD8_CMR_QUADRATURE_X2;
283f1d8a071SWilliam Breathitt Gray 			break;
284aaec1a0fSWilliam Breathitt Gray 		case COUNTER_FUNCTION_QUADRATURE_X4:
285f1d8a071SWilliam Breathitt Gray 			*scale = 2;
286f1d8a071SWilliam Breathitt Gray 			mode_cfg |= QUAD8_CMR_QUADRATURE_X4;
287f1d8a071SWilliam Breathitt Gray 			break;
288b11eed15SWilliam Breathitt Gray 		default:
289b11eed15SWilliam Breathitt Gray 			/* should never reach this path */
29009db4678SWilliam Breathitt Gray 			spin_unlock_irqrestore(&priv->lock, irqflags);
291b11eed15SWilliam Breathitt Gray 			return -EINVAL;
292f1d8a071SWilliam Breathitt Gray 		}
293f1d8a071SWilliam Breathitt Gray 	}
294f1d8a071SWilliam Breathitt Gray 
295f1d8a071SWilliam Breathitt Gray 	/* Load mode configuration to Counter Mode Register */
296f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
297f1d8a071SWilliam Breathitt Gray 
29809db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
299fc069262SSyed Nayyar Waris 
300f1d8a071SWilliam Breathitt Gray 	return 0;
301f1d8a071SWilliam Breathitt Gray }
302f1d8a071SWilliam Breathitt Gray 
303aaec1a0fSWilliam Breathitt Gray static int quad8_direction_read(struct counter_device *counter,
304aaec1a0fSWilliam Breathitt Gray 				struct counter_count *count,
305aaec1a0fSWilliam Breathitt Gray 				enum counter_count_direction *direction)
306f1d8a071SWilliam Breathitt Gray {
307*aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
308f1d8a071SWilliam Breathitt Gray 	unsigned int ud_flag;
309f1d8a071SWilliam Breathitt Gray 	const unsigned int flag_addr = priv->base + 2 * count->id + 1;
310f1d8a071SWilliam Breathitt Gray 
311f1d8a071SWilliam Breathitt Gray 	/* U/D flag: nonzero = up, zero = down */
312f1d8a071SWilliam Breathitt Gray 	ud_flag = inb(flag_addr) & QUAD8_FLAG_UD;
313f1d8a071SWilliam Breathitt Gray 
314f1d8a071SWilliam Breathitt Gray 	*direction = (ud_flag) ? COUNTER_COUNT_DIRECTION_FORWARD :
315f1d8a071SWilliam Breathitt Gray 		COUNTER_COUNT_DIRECTION_BACKWARD;
316aaec1a0fSWilliam Breathitt Gray 
317aaec1a0fSWilliam Breathitt Gray 	return 0;
318f1d8a071SWilliam Breathitt Gray }
319f1d8a071SWilliam Breathitt Gray 
3206a9eb0e3SWilliam Breathitt Gray static const enum counter_synapse_action quad8_index_actions_list[] = {
321aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_NONE,
322aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
323f1d8a071SWilliam Breathitt Gray };
324f1d8a071SWilliam Breathitt Gray 
3256a9eb0e3SWilliam Breathitt Gray static const enum counter_synapse_action quad8_synapse_actions_list[] = {
326aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_NONE,
327aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
328aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
329aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
330f1d8a071SWilliam Breathitt Gray };
331f1d8a071SWilliam Breathitt Gray 
332aaec1a0fSWilliam Breathitt Gray static int quad8_action_read(struct counter_device *counter,
333aaec1a0fSWilliam Breathitt Gray 			     struct counter_count *count,
334aaec1a0fSWilliam Breathitt Gray 			     struct counter_synapse *synapse,
335aaec1a0fSWilliam Breathitt Gray 			     enum counter_synapse_action *action)
336f1d8a071SWilliam Breathitt Gray {
337*aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
338f1d8a071SWilliam Breathitt Gray 	int err;
339aaec1a0fSWilliam Breathitt Gray 	enum counter_function function;
340f1d8a071SWilliam Breathitt Gray 	const size_t signal_a_id = count->synapses[0].signal->id;
341f1d8a071SWilliam Breathitt Gray 	enum counter_count_direction direction;
342f1d8a071SWilliam Breathitt Gray 
343f1d8a071SWilliam Breathitt Gray 	/* Handle Index signals */
344f1d8a071SWilliam Breathitt Gray 	if (synapse->signal->id >= 16) {
345f1d8a071SWilliam Breathitt Gray 		if (priv->preset_enable[count->id])
346aaec1a0fSWilliam Breathitt Gray 			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
347f1d8a071SWilliam Breathitt Gray 		else
348aaec1a0fSWilliam Breathitt Gray 			*action = COUNTER_SYNAPSE_ACTION_NONE;
349f1d8a071SWilliam Breathitt Gray 
350f1d8a071SWilliam Breathitt Gray 		return 0;
351f1d8a071SWilliam Breathitt Gray 	}
352f1d8a071SWilliam Breathitt Gray 
353aaec1a0fSWilliam Breathitt Gray 	err = quad8_function_read(counter, count, &function);
354f1d8a071SWilliam Breathitt Gray 	if (err)
355f1d8a071SWilliam Breathitt Gray 		return err;
356f1d8a071SWilliam Breathitt Gray 
357f1d8a071SWilliam Breathitt Gray 	/* Default action mode */
358aaec1a0fSWilliam Breathitt Gray 	*action = COUNTER_SYNAPSE_ACTION_NONE;
359f1d8a071SWilliam Breathitt Gray 
360f1d8a071SWilliam Breathitt Gray 	/* Determine action mode based on current count function mode */
361f1d8a071SWilliam Breathitt Gray 	switch (function) {
362aaec1a0fSWilliam Breathitt Gray 	case COUNTER_FUNCTION_PULSE_DIRECTION:
363f1d8a071SWilliam Breathitt Gray 		if (synapse->signal->id == signal_a_id)
364aaec1a0fSWilliam Breathitt Gray 			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
365b11eed15SWilliam Breathitt Gray 		return 0;
366aaec1a0fSWilliam Breathitt Gray 	case COUNTER_FUNCTION_QUADRATURE_X1_A:
367f1d8a071SWilliam Breathitt Gray 		if (synapse->signal->id == signal_a_id) {
368aaec1a0fSWilliam Breathitt Gray 			err = quad8_direction_read(counter, count, &direction);
369aaec1a0fSWilliam Breathitt Gray 			if (err)
370aaec1a0fSWilliam Breathitt Gray 				return err;
371f1d8a071SWilliam Breathitt Gray 
372f1d8a071SWilliam Breathitt Gray 			if (direction == COUNTER_COUNT_DIRECTION_FORWARD)
373aaec1a0fSWilliam Breathitt Gray 				*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
374f1d8a071SWilliam Breathitt Gray 			else
375aaec1a0fSWilliam Breathitt Gray 				*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
376f1d8a071SWilliam Breathitt Gray 		}
377b11eed15SWilliam Breathitt Gray 		return 0;
378aaec1a0fSWilliam Breathitt Gray 	case COUNTER_FUNCTION_QUADRATURE_X2_A:
379f1d8a071SWilliam Breathitt Gray 		if (synapse->signal->id == signal_a_id)
380aaec1a0fSWilliam Breathitt Gray 			*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
381b11eed15SWilliam Breathitt Gray 		return 0;
382aaec1a0fSWilliam Breathitt Gray 	case COUNTER_FUNCTION_QUADRATURE_X4:
383aaec1a0fSWilliam Breathitt Gray 		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
384f1d8a071SWilliam Breathitt Gray 		return 0;
385b11eed15SWilliam Breathitt Gray 	default:
386b11eed15SWilliam Breathitt Gray 		/* should never reach this path */
387b11eed15SWilliam Breathitt Gray 		return -EINVAL;
388b11eed15SWilliam Breathitt Gray 	}
389f1d8a071SWilliam Breathitt Gray }
390f1d8a071SWilliam Breathitt Gray 
3917aa2ba0dSWilliam Breathitt Gray enum {
3927aa2ba0dSWilliam Breathitt Gray 	QUAD8_EVENT_CARRY = 0,
3937aa2ba0dSWilliam Breathitt Gray 	QUAD8_EVENT_COMPARE = 1,
3947aa2ba0dSWilliam Breathitt Gray 	QUAD8_EVENT_CARRY_BORROW = 2,
3957aa2ba0dSWilliam Breathitt Gray 	QUAD8_EVENT_INDEX = 3,
3967aa2ba0dSWilliam Breathitt Gray };
3977aa2ba0dSWilliam Breathitt Gray 
3987aa2ba0dSWilliam Breathitt Gray static int quad8_events_configure(struct counter_device *counter)
3997aa2ba0dSWilliam Breathitt Gray {
400*aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
4017aa2ba0dSWilliam Breathitt Gray 	unsigned long irq_enabled = 0;
4027aa2ba0dSWilliam Breathitt Gray 	unsigned long irqflags;
403c95cc0d9SWilliam Breathitt Gray 	struct counter_event_node *event_node;
404c95cc0d9SWilliam Breathitt Gray 	unsigned int next_irq_trigger;
4057aa2ba0dSWilliam Breathitt Gray 	unsigned long ior_cfg;
4067aa2ba0dSWilliam Breathitt Gray 	unsigned long base_offset;
4077aa2ba0dSWilliam Breathitt Gray 
4087aa2ba0dSWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
4097aa2ba0dSWilliam Breathitt Gray 
410c95cc0d9SWilliam Breathitt Gray 	list_for_each_entry(event_node, &counter->events_list, l) {
411c95cc0d9SWilliam Breathitt Gray 		switch (event_node->event) {
412c95cc0d9SWilliam Breathitt Gray 		case COUNTER_EVENT_OVERFLOW:
413c95cc0d9SWilliam Breathitt Gray 			next_irq_trigger = QUAD8_EVENT_CARRY;
414c95cc0d9SWilliam Breathitt Gray 			break;
415c95cc0d9SWilliam Breathitt Gray 		case COUNTER_EVENT_THRESHOLD:
416c95cc0d9SWilliam Breathitt Gray 			next_irq_trigger = QUAD8_EVENT_COMPARE;
417c95cc0d9SWilliam Breathitt Gray 			break;
418c95cc0d9SWilliam Breathitt Gray 		case COUNTER_EVENT_OVERFLOW_UNDERFLOW:
419c95cc0d9SWilliam Breathitt Gray 			next_irq_trigger = QUAD8_EVENT_CARRY_BORROW;
420c95cc0d9SWilliam Breathitt Gray 			break;
421c95cc0d9SWilliam Breathitt Gray 		case COUNTER_EVENT_INDEX:
422c95cc0d9SWilliam Breathitt Gray 			next_irq_trigger = QUAD8_EVENT_INDEX;
423c95cc0d9SWilliam Breathitt Gray 			break;
424c95cc0d9SWilliam Breathitt Gray 		default:
425c95cc0d9SWilliam Breathitt Gray 			/* should never reach this path */
426c95cc0d9SWilliam Breathitt Gray 			spin_unlock_irqrestore(&priv->lock, irqflags);
427c95cc0d9SWilliam Breathitt Gray 			return -EINVAL;
4287aa2ba0dSWilliam Breathitt Gray 		}
4297aa2ba0dSWilliam Breathitt Gray 
430c95cc0d9SWilliam Breathitt Gray 		/* Skip configuration if it is the same as previously set */
431c95cc0d9SWilliam Breathitt Gray 		if (priv->irq_trigger[event_node->channel] == next_irq_trigger)
432c95cc0d9SWilliam Breathitt Gray 			continue;
433c95cc0d9SWilliam Breathitt Gray 
434c95cc0d9SWilliam Breathitt Gray 		/* Save new IRQ function configuration */
435c95cc0d9SWilliam Breathitt Gray 		priv->irq_trigger[event_node->channel] = next_irq_trigger;
436c95cc0d9SWilliam Breathitt Gray 
437c95cc0d9SWilliam Breathitt Gray 		/* Load configuration to I/O Control Register */
438c95cc0d9SWilliam Breathitt Gray 		ior_cfg = priv->ab_enable[event_node->channel] |
439c95cc0d9SWilliam Breathitt Gray 			  priv->preset_enable[event_node->channel] << 1 |
440c95cc0d9SWilliam Breathitt Gray 			  priv->irq_trigger[event_node->channel] << 3;
441c95cc0d9SWilliam Breathitt Gray 		base_offset = priv->base + 2 * event_node->channel + 1;
442c95cc0d9SWilliam Breathitt Gray 		outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
4437aa2ba0dSWilliam Breathitt Gray 
4447aa2ba0dSWilliam Breathitt Gray 		/* Enable IRQ line */
445c95cc0d9SWilliam Breathitt Gray 		irq_enabled |= BIT(event_node->channel);
4467aa2ba0dSWilliam Breathitt Gray 	}
4477aa2ba0dSWilliam Breathitt Gray 
4487aa2ba0dSWilliam Breathitt Gray 	outb(irq_enabled, priv->base + QUAD8_REG_INDEX_INTERRUPT);
4497aa2ba0dSWilliam Breathitt Gray 
4507aa2ba0dSWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
4517aa2ba0dSWilliam Breathitt Gray 
4527aa2ba0dSWilliam Breathitt Gray 	return 0;
4537aa2ba0dSWilliam Breathitt Gray }
4547aa2ba0dSWilliam Breathitt Gray 
4557aa2ba0dSWilliam Breathitt Gray static int quad8_watch_validate(struct counter_device *counter,
4567aa2ba0dSWilliam Breathitt Gray 				const struct counter_watch *watch)
4577aa2ba0dSWilliam Breathitt Gray {
458c95cc0d9SWilliam Breathitt Gray 	struct counter_event_node *event_node;
4597aa2ba0dSWilliam Breathitt Gray 
4607aa2ba0dSWilliam Breathitt Gray 	if (watch->channel > QUAD8_NUM_COUNTERS - 1)
4617aa2ba0dSWilliam Breathitt Gray 		return -EINVAL;
4627aa2ba0dSWilliam Breathitt Gray 
4637aa2ba0dSWilliam Breathitt Gray 	switch (watch->event) {
4647aa2ba0dSWilliam Breathitt Gray 	case COUNTER_EVENT_OVERFLOW:
4657aa2ba0dSWilliam Breathitt Gray 	case COUNTER_EVENT_THRESHOLD:
4667aa2ba0dSWilliam Breathitt Gray 	case COUNTER_EVENT_OVERFLOW_UNDERFLOW:
4677aa2ba0dSWilliam Breathitt Gray 	case COUNTER_EVENT_INDEX:
468c95cc0d9SWilliam Breathitt Gray 		list_for_each_entry(event_node, &counter->next_events_list, l)
469c95cc0d9SWilliam Breathitt Gray 			if (watch->channel == event_node->channel &&
470c95cc0d9SWilliam Breathitt Gray 				watch->event != event_node->event)
4717aa2ba0dSWilliam Breathitt Gray 				return -EINVAL;
4727aa2ba0dSWilliam Breathitt Gray 		return 0;
4737aa2ba0dSWilliam Breathitt Gray 	default:
4747aa2ba0dSWilliam Breathitt Gray 		return -EINVAL;
4757aa2ba0dSWilliam Breathitt Gray 	}
4767aa2ba0dSWilliam Breathitt Gray }
4777aa2ba0dSWilliam Breathitt Gray 
47817aa207eSYueHaibing static const struct counter_ops quad8_ops = {
479f1d8a071SWilliam Breathitt Gray 	.signal_read = quad8_signal_read,
480f1d8a071SWilliam Breathitt Gray 	.count_read = quad8_count_read,
481f1d8a071SWilliam Breathitt Gray 	.count_write = quad8_count_write,
482aaec1a0fSWilliam Breathitt Gray 	.function_read = quad8_function_read,
483aaec1a0fSWilliam Breathitt Gray 	.function_write = quad8_function_write,
4847aa2ba0dSWilliam Breathitt Gray 	.action_read = quad8_action_read,
4857aa2ba0dSWilliam Breathitt Gray 	.events_configure = quad8_events_configure,
4867aa2ba0dSWilliam Breathitt Gray 	.watch_validate = quad8_watch_validate,
487f1d8a071SWilliam Breathitt Gray };
488f1d8a071SWilliam Breathitt Gray 
489e357e81fSWilliam Breathitt Gray static const char *const quad8_index_polarity_modes[] = {
490e357e81fSWilliam Breathitt Gray 	"negative",
491e357e81fSWilliam Breathitt Gray 	"positive"
492e357e81fSWilliam Breathitt Gray };
493e357e81fSWilliam Breathitt Gray 
494f1d8a071SWilliam Breathitt Gray static int quad8_index_polarity_get(struct counter_device *counter,
495aaec1a0fSWilliam Breathitt Gray 				    struct counter_signal *signal,
496aaec1a0fSWilliam Breathitt Gray 				    u32 *index_polarity)
497f1d8a071SWilliam Breathitt Gray {
498*aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
499f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
500f1d8a071SWilliam Breathitt Gray 
501f1d8a071SWilliam Breathitt Gray 	*index_polarity = priv->index_polarity[channel_id];
502f1d8a071SWilliam Breathitt Gray 
503f1d8a071SWilliam Breathitt Gray 	return 0;
504f1d8a071SWilliam Breathitt Gray }
505f1d8a071SWilliam Breathitt Gray 
506f1d8a071SWilliam Breathitt Gray static int quad8_index_polarity_set(struct counter_device *counter,
507aaec1a0fSWilliam Breathitt Gray 				    struct counter_signal *signal,
508aaec1a0fSWilliam Breathitt Gray 				    u32 index_polarity)
509f1d8a071SWilliam Breathitt Gray {
510*aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
511f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
512f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * channel_id + 1;
51309db4678SWilliam Breathitt Gray 	unsigned long irqflags;
514fc069262SSyed Nayyar Waris 	unsigned int idr_cfg = index_polarity << 1;
515fc069262SSyed Nayyar Waris 
51609db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
517fc069262SSyed Nayyar Waris 
518fc069262SSyed Nayyar Waris 	idr_cfg |= priv->synchronous_mode[channel_id];
519f1d8a071SWilliam Breathitt Gray 
520f1d8a071SWilliam Breathitt Gray 	priv->index_polarity[channel_id] = index_polarity;
521f1d8a071SWilliam Breathitt Gray 
522f1d8a071SWilliam Breathitt Gray 	/* Load Index Control configuration to Index Control Register */
523f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
524f1d8a071SWilliam Breathitt Gray 
52509db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
526fc069262SSyed Nayyar Waris 
527f1d8a071SWilliam Breathitt Gray 	return 0;
528f1d8a071SWilliam Breathitt Gray }
529f1d8a071SWilliam Breathitt Gray 
530e357e81fSWilliam Breathitt Gray static const char *const quad8_synchronous_modes[] = {
531e357e81fSWilliam Breathitt Gray 	"non-synchronous",
532e357e81fSWilliam Breathitt Gray 	"synchronous"
533e357e81fSWilliam Breathitt Gray };
534e357e81fSWilliam Breathitt Gray 
535f1d8a071SWilliam Breathitt Gray static int quad8_synchronous_mode_get(struct counter_device *counter,
536aaec1a0fSWilliam Breathitt Gray 				      struct counter_signal *signal,
537aaec1a0fSWilliam Breathitt Gray 				      u32 *synchronous_mode)
538f1d8a071SWilliam Breathitt Gray {
539*aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
540f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
541f1d8a071SWilliam Breathitt Gray 
542f1d8a071SWilliam Breathitt Gray 	*synchronous_mode = priv->synchronous_mode[channel_id];
543f1d8a071SWilliam Breathitt Gray 
544f1d8a071SWilliam Breathitt Gray 	return 0;
545f1d8a071SWilliam Breathitt Gray }
546f1d8a071SWilliam Breathitt Gray 
547f1d8a071SWilliam Breathitt Gray static int quad8_synchronous_mode_set(struct counter_device *counter,
548aaec1a0fSWilliam Breathitt Gray 				      struct counter_signal *signal,
549aaec1a0fSWilliam Breathitt Gray 				      u32 synchronous_mode)
550f1d8a071SWilliam Breathitt Gray {
551*aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
552f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
553f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * channel_id + 1;
55409db4678SWilliam Breathitt Gray 	unsigned long irqflags;
555fc069262SSyed Nayyar Waris 	unsigned int idr_cfg = synchronous_mode;
556fc069262SSyed Nayyar Waris 
55709db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
558fc069262SSyed Nayyar Waris 
559fc069262SSyed Nayyar Waris 	idr_cfg |= priv->index_polarity[channel_id] << 1;
560f1d8a071SWilliam Breathitt Gray 
561f1d8a071SWilliam Breathitt Gray 	/* Index function must be non-synchronous in non-quadrature mode */
562fc069262SSyed Nayyar Waris 	if (synchronous_mode && !priv->quadrature_mode[channel_id]) {
56309db4678SWilliam Breathitt Gray 		spin_unlock_irqrestore(&priv->lock, irqflags);
564f1d8a071SWilliam Breathitt Gray 		return -EINVAL;
565fc069262SSyed Nayyar Waris 	}
566f1d8a071SWilliam Breathitt Gray 
567f1d8a071SWilliam Breathitt Gray 	priv->synchronous_mode[channel_id] = synchronous_mode;
568f1d8a071SWilliam Breathitt Gray 
569f1d8a071SWilliam Breathitt Gray 	/* Load Index Control configuration to Index Control Register */
570f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
571f1d8a071SWilliam Breathitt Gray 
57209db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
573fc069262SSyed Nayyar Waris 
574f1d8a071SWilliam Breathitt Gray 	return 0;
575f1d8a071SWilliam Breathitt Gray }
576f1d8a071SWilliam Breathitt Gray 
577aaec1a0fSWilliam Breathitt Gray static int quad8_count_floor_read(struct counter_device *counter,
578aaec1a0fSWilliam Breathitt Gray 				  struct counter_count *count, u64 *floor)
579f1d8a071SWilliam Breathitt Gray {
580f1d8a071SWilliam Breathitt Gray 	/* Only a floor of 0 is supported */
581aaec1a0fSWilliam Breathitt Gray 	*floor = 0;
582aaec1a0fSWilliam Breathitt Gray 
583aaec1a0fSWilliam Breathitt Gray 	return 0;
584f1d8a071SWilliam Breathitt Gray }
585f1d8a071SWilliam Breathitt Gray 
586aaec1a0fSWilliam Breathitt Gray static int quad8_count_mode_read(struct counter_device *counter,
587aaec1a0fSWilliam Breathitt Gray 				 struct counter_count *count,
588aaec1a0fSWilliam Breathitt Gray 				 enum counter_count_mode *cnt_mode)
589f1d8a071SWilliam Breathitt Gray {
590*aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
591f1d8a071SWilliam Breathitt Gray 
592f1d8a071SWilliam Breathitt Gray 	/* Map 104-QUAD-8 count mode to Generic Counter count mode */
593f1d8a071SWilliam Breathitt Gray 	switch (priv->count_mode[count->id]) {
594f1d8a071SWilliam Breathitt Gray 	case 0:
595f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_NORMAL;
596f1d8a071SWilliam Breathitt Gray 		break;
597f1d8a071SWilliam Breathitt Gray 	case 1:
598f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_RANGE_LIMIT;
599f1d8a071SWilliam Breathitt Gray 		break;
600f1d8a071SWilliam Breathitt Gray 	case 2:
601f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_NON_RECYCLE;
602f1d8a071SWilliam Breathitt Gray 		break;
603f1d8a071SWilliam Breathitt Gray 	case 3:
604f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_MODULO_N;
605f1d8a071SWilliam Breathitt Gray 		break;
606f1d8a071SWilliam Breathitt Gray 	}
607f1d8a071SWilliam Breathitt Gray 
608f1d8a071SWilliam Breathitt Gray 	return 0;
609f1d8a071SWilliam Breathitt Gray }
610f1d8a071SWilliam Breathitt Gray 
611aaec1a0fSWilliam Breathitt Gray static int quad8_count_mode_write(struct counter_device *counter,
612aaec1a0fSWilliam Breathitt Gray 				  struct counter_count *count,
613aaec1a0fSWilliam Breathitt Gray 				  enum counter_count_mode cnt_mode)
614f1d8a071SWilliam Breathitt Gray {
615*aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
616aaec1a0fSWilliam Breathitt Gray 	unsigned int count_mode;
617f1d8a071SWilliam Breathitt Gray 	unsigned int mode_cfg;
618f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id + 1;
61909db4678SWilliam Breathitt Gray 	unsigned long irqflags;
620f1d8a071SWilliam Breathitt Gray 
621f1d8a071SWilliam Breathitt Gray 	/* Map Generic Counter count mode to 104-QUAD-8 count mode */
622f1d8a071SWilliam Breathitt Gray 	switch (cnt_mode) {
623f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_NORMAL:
624aaec1a0fSWilliam Breathitt Gray 		count_mode = 0;
625f1d8a071SWilliam Breathitt Gray 		break;
626f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_RANGE_LIMIT:
627aaec1a0fSWilliam Breathitt Gray 		count_mode = 1;
628f1d8a071SWilliam Breathitt Gray 		break;
629f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_NON_RECYCLE:
630aaec1a0fSWilliam Breathitt Gray 		count_mode = 2;
631f1d8a071SWilliam Breathitt Gray 		break;
632f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_MODULO_N:
633aaec1a0fSWilliam Breathitt Gray 		count_mode = 3;
634f1d8a071SWilliam Breathitt Gray 		break;
635b11eed15SWilliam Breathitt Gray 	default:
636b11eed15SWilliam Breathitt Gray 		/* should never reach this path */
637b11eed15SWilliam Breathitt Gray 		return -EINVAL;
638f1d8a071SWilliam Breathitt Gray 	}
639f1d8a071SWilliam Breathitt Gray 
64009db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
641fc069262SSyed Nayyar Waris 
642aaec1a0fSWilliam Breathitt Gray 	priv->count_mode[count->id] = count_mode;
643f1d8a071SWilliam Breathitt Gray 
644f1d8a071SWilliam Breathitt Gray 	/* Set count mode configuration value */
645aaec1a0fSWilliam Breathitt Gray 	mode_cfg = count_mode << 1;
646f1d8a071SWilliam Breathitt Gray 
647f1d8a071SWilliam Breathitt Gray 	/* Add quadrature mode configuration */
648f1d8a071SWilliam Breathitt Gray 	if (priv->quadrature_mode[count->id])
649f1d8a071SWilliam Breathitt Gray 		mode_cfg |= (priv->quadrature_scale[count->id] + 1) << 3;
650f1d8a071SWilliam Breathitt Gray 
651f1d8a071SWilliam Breathitt Gray 	/* Load mode configuration to Counter Mode Register */
652f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
653f1d8a071SWilliam Breathitt Gray 
65409db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
655fc069262SSyed Nayyar Waris 
656f1d8a071SWilliam Breathitt Gray 	return 0;
657f1d8a071SWilliam Breathitt Gray }
658f1d8a071SWilliam Breathitt Gray 
659aaec1a0fSWilliam Breathitt Gray static int quad8_count_enable_read(struct counter_device *counter,
660aaec1a0fSWilliam Breathitt Gray 				   struct counter_count *count, u8 *enable)
661f1d8a071SWilliam Breathitt Gray {
662*aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
663f1d8a071SWilliam Breathitt Gray 
664aaec1a0fSWilliam Breathitt Gray 	*enable = priv->ab_enable[count->id];
665aaec1a0fSWilliam Breathitt Gray 
666aaec1a0fSWilliam Breathitt Gray 	return 0;
667f1d8a071SWilliam Breathitt Gray }
668f1d8a071SWilliam Breathitt Gray 
669aaec1a0fSWilliam Breathitt Gray static int quad8_count_enable_write(struct counter_device *counter,
670aaec1a0fSWilliam Breathitt Gray 				    struct counter_count *count, u8 enable)
671f1d8a071SWilliam Breathitt Gray {
672*aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
673f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id;
67409db4678SWilliam Breathitt Gray 	unsigned long irqflags;
675f1d8a071SWilliam Breathitt Gray 	unsigned int ior_cfg;
676f1d8a071SWilliam Breathitt Gray 
67709db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
678fc069262SSyed Nayyar Waris 
679aaec1a0fSWilliam Breathitt Gray 	priv->ab_enable[count->id] = enable;
680f1d8a071SWilliam Breathitt Gray 
6817aa2ba0dSWilliam Breathitt Gray 	ior_cfg = enable | priv->preset_enable[count->id] << 1 |
6827aa2ba0dSWilliam Breathitt Gray 		  priv->irq_trigger[count->id] << 3;
683f1d8a071SWilliam Breathitt Gray 
684f1d8a071SWilliam Breathitt Gray 	/* Load I/O control configuration */
685f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
686f1d8a071SWilliam Breathitt Gray 
68709db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
688fc069262SSyed Nayyar Waris 
689aaec1a0fSWilliam Breathitt Gray 	return 0;
690f1d8a071SWilliam Breathitt Gray }
691f1d8a071SWilliam Breathitt Gray 
692e357e81fSWilliam Breathitt Gray static const char *const quad8_noise_error_states[] = {
693e357e81fSWilliam Breathitt Gray 	"No excessive noise is present at the count inputs",
694e357e81fSWilliam Breathitt Gray 	"Excessive noise is present at the count inputs"
695e357e81fSWilliam Breathitt Gray };
696e357e81fSWilliam Breathitt Gray 
697f1d8a071SWilliam Breathitt Gray static int quad8_error_noise_get(struct counter_device *counter,
698aaec1a0fSWilliam Breathitt Gray 				 struct counter_count *count, u32 *noise_error)
699f1d8a071SWilliam Breathitt Gray {
700*aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
701f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id + 1;
702f1d8a071SWilliam Breathitt Gray 
703f1d8a071SWilliam Breathitt Gray 	*noise_error = !!(inb(base_offset) & QUAD8_FLAG_E);
704f1d8a071SWilliam Breathitt Gray 
705f1d8a071SWilliam Breathitt Gray 	return 0;
706f1d8a071SWilliam Breathitt Gray }
707f1d8a071SWilliam Breathitt Gray 
708aaec1a0fSWilliam Breathitt Gray static int quad8_count_preset_read(struct counter_device *counter,
709aaec1a0fSWilliam Breathitt Gray 				   struct counter_count *count, u64 *preset)
710f1d8a071SWilliam Breathitt Gray {
711*aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
712f1d8a071SWilliam Breathitt Gray 
713aaec1a0fSWilliam Breathitt Gray 	*preset = priv->preset[count->id];
714aaec1a0fSWilliam Breathitt Gray 
715aaec1a0fSWilliam Breathitt Gray 	return 0;
716f1d8a071SWilliam Breathitt Gray }
717f1d8a071SWilliam Breathitt Gray 
718e612b600SWilliam Breathitt Gray static void quad8_preset_register_set(struct quad8 *const priv, const int id,
719e612b600SWilliam Breathitt Gray 				      const unsigned int preset)
720fc069262SSyed Nayyar Waris {
721e357e81fSWilliam Breathitt Gray 	const unsigned int base_offset = priv->base + 2 * id;
722fc069262SSyed Nayyar Waris 	int i;
723fc069262SSyed Nayyar Waris 
724e357e81fSWilliam Breathitt Gray 	priv->preset[id] = preset;
725fc069262SSyed Nayyar Waris 
726fc069262SSyed Nayyar Waris 	/* Reset Byte Pointer */
727fc069262SSyed Nayyar Waris 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
728fc069262SSyed Nayyar Waris 
729fc069262SSyed Nayyar Waris 	/* Set Preset Register */
730fc069262SSyed Nayyar Waris 	for (i = 0; i < 3; i++)
731fc069262SSyed Nayyar Waris 		outb(preset >> (8 * i), base_offset);
732fc069262SSyed Nayyar Waris }
733fc069262SSyed Nayyar Waris 
734aaec1a0fSWilliam Breathitt Gray static int quad8_count_preset_write(struct counter_device *counter,
735aaec1a0fSWilliam Breathitt Gray 				    struct counter_count *count, u64 preset)
736f1d8a071SWilliam Breathitt Gray {
737*aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
73809db4678SWilliam Breathitt Gray 	unsigned long irqflags;
739f1d8a071SWilliam Breathitt Gray 
740f1d8a071SWilliam Breathitt Gray 	/* Only 24-bit values are supported */
741f1d8a071SWilliam Breathitt Gray 	if (preset > 0xFFFFFF)
742e2ff3198SWilliam Breathitt Gray 		return -ERANGE;
743f1d8a071SWilliam Breathitt Gray 
74409db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
745f1d8a071SWilliam Breathitt Gray 
746fc069262SSyed Nayyar Waris 	quad8_preset_register_set(priv, count->id, preset);
747f1d8a071SWilliam Breathitt Gray 
74809db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
749f1d8a071SWilliam Breathitt Gray 
750aaec1a0fSWilliam Breathitt Gray 	return 0;
751f1d8a071SWilliam Breathitt Gray }
752f1d8a071SWilliam Breathitt Gray 
753aaec1a0fSWilliam Breathitt Gray static int quad8_count_ceiling_read(struct counter_device *counter,
754aaec1a0fSWilliam Breathitt Gray 				    struct counter_count *count, u64 *ceiling)
755f1d8a071SWilliam Breathitt Gray {
756*aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
75709db4678SWilliam Breathitt Gray 	unsigned long irqflags;
758fc069262SSyed Nayyar Waris 
75909db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
760f1d8a071SWilliam Breathitt Gray 
761f1d8a071SWilliam Breathitt Gray 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
762f1d8a071SWilliam Breathitt Gray 	switch (priv->count_mode[count->id]) {
763f1d8a071SWilliam Breathitt Gray 	case 1:
764f1d8a071SWilliam Breathitt Gray 	case 3:
765aaec1a0fSWilliam Breathitt Gray 		*ceiling = priv->preset[count->id];
766aaec1a0fSWilliam Breathitt Gray 		break;
767aaec1a0fSWilliam Breathitt Gray 	default:
768f1d8a071SWilliam Breathitt Gray 		/* By default 0x1FFFFFF (25 bits unsigned) is maximum count */
769aaec1a0fSWilliam Breathitt Gray 		*ceiling = 0x1FFFFFF;
770aaec1a0fSWilliam Breathitt Gray 		break;
771f1d8a071SWilliam Breathitt Gray 	}
772f1d8a071SWilliam Breathitt Gray 
77309db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
774aaec1a0fSWilliam Breathitt Gray 
775aaec1a0fSWilliam Breathitt Gray 	return 0;
776aaec1a0fSWilliam Breathitt Gray }
777aaec1a0fSWilliam Breathitt Gray 
778aaec1a0fSWilliam Breathitt Gray static int quad8_count_ceiling_write(struct counter_device *counter,
779aaec1a0fSWilliam Breathitt Gray 				     struct counter_count *count, u64 ceiling)
780f1d8a071SWilliam Breathitt Gray {
781*aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
78209db4678SWilliam Breathitt Gray 	unsigned long irqflags;
783fc069262SSyed Nayyar Waris 
784fc069262SSyed Nayyar Waris 	/* Only 24-bit values are supported */
785fc069262SSyed Nayyar Waris 	if (ceiling > 0xFFFFFF)
786e2ff3198SWilliam Breathitt Gray 		return -ERANGE;
787fc069262SSyed Nayyar Waris 
78809db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
789f1d8a071SWilliam Breathitt Gray 
790f1d8a071SWilliam Breathitt Gray 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
791f1d8a071SWilliam Breathitt Gray 	switch (priv->count_mode[count->id]) {
792f1d8a071SWilliam Breathitt Gray 	case 1:
793f1d8a071SWilliam Breathitt Gray 	case 3:
794fc069262SSyed Nayyar Waris 		quad8_preset_register_set(priv, count->id, ceiling);
79509db4678SWilliam Breathitt Gray 		spin_unlock_irqrestore(&priv->lock, irqflags);
796aaec1a0fSWilliam Breathitt Gray 		return 0;
797f1d8a071SWilliam Breathitt Gray 	}
798f1d8a071SWilliam Breathitt Gray 
79909db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
800fc069262SSyed Nayyar Waris 
801728246e8SWilliam Breathitt Gray 	return -EINVAL;
802f1d8a071SWilliam Breathitt Gray }
803f1d8a071SWilliam Breathitt Gray 
804aaec1a0fSWilliam Breathitt Gray static int quad8_count_preset_enable_read(struct counter_device *counter,
805aaec1a0fSWilliam Breathitt Gray 					  struct counter_count *count,
806aaec1a0fSWilliam Breathitt Gray 					  u8 *preset_enable)
807f1d8a071SWilliam Breathitt Gray {
808*aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
809f1d8a071SWilliam Breathitt Gray 
810aaec1a0fSWilliam Breathitt Gray 	*preset_enable = !priv->preset_enable[count->id];
811aaec1a0fSWilliam Breathitt Gray 
812aaec1a0fSWilliam Breathitt Gray 	return 0;
813f1d8a071SWilliam Breathitt Gray }
814f1d8a071SWilliam Breathitt Gray 
815aaec1a0fSWilliam Breathitt Gray static int quad8_count_preset_enable_write(struct counter_device *counter,
816aaec1a0fSWilliam Breathitt Gray 					   struct counter_count *count,
817aaec1a0fSWilliam Breathitt Gray 					   u8 preset_enable)
818f1d8a071SWilliam Breathitt Gray {
819*aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
820f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id + 1;
82109db4678SWilliam Breathitt Gray 	unsigned long irqflags;
822f1d8a071SWilliam Breathitt Gray 	unsigned int ior_cfg;
823f1d8a071SWilliam Breathitt Gray 
824f1d8a071SWilliam Breathitt Gray 	/* Preset enable is active low in Input/Output Control register */
825f1d8a071SWilliam Breathitt Gray 	preset_enable = !preset_enable;
826f1d8a071SWilliam Breathitt Gray 
82709db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
828fc069262SSyed Nayyar Waris 
829f1d8a071SWilliam Breathitt Gray 	priv->preset_enable[count->id] = preset_enable;
830f1d8a071SWilliam Breathitt Gray 
8317aa2ba0dSWilliam Breathitt Gray 	ior_cfg = priv->ab_enable[count->id] | preset_enable << 1 |
8327aa2ba0dSWilliam Breathitt Gray 		  priv->irq_trigger[count->id] << 3;
833f1d8a071SWilliam Breathitt Gray 
834f1d8a071SWilliam Breathitt Gray 	/* Load I/O control configuration to Input / Output Control Register */
835f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
836f1d8a071SWilliam Breathitt Gray 
83709db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
838fc069262SSyed Nayyar Waris 
839aaec1a0fSWilliam Breathitt Gray 	return 0;
840f1d8a071SWilliam Breathitt Gray }
841f1d8a071SWilliam Breathitt Gray 
842aaec1a0fSWilliam Breathitt Gray static int quad8_signal_cable_fault_read(struct counter_device *counter,
843954ab5ccSWilliam Breathitt Gray 					 struct counter_signal *signal,
844aaec1a0fSWilliam Breathitt Gray 					 u8 *cable_fault)
845954ab5ccSWilliam Breathitt Gray {
846*aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
847954ab5ccSWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
84809db4678SWilliam Breathitt Gray 	unsigned long irqflags;
849708d9893SSyed Nayyar Waris 	bool disabled;
850954ab5ccSWilliam Breathitt Gray 	unsigned int status;
851954ab5ccSWilliam Breathitt Gray 
85209db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
853708d9893SSyed Nayyar Waris 
854708d9893SSyed Nayyar Waris 	disabled = !(priv->cable_fault_enable & BIT(channel_id));
855708d9893SSyed Nayyar Waris 
856708d9893SSyed Nayyar Waris 	if (disabled) {
85709db4678SWilliam Breathitt Gray 		spin_unlock_irqrestore(&priv->lock, irqflags);
858954ab5ccSWilliam Breathitt Gray 		return -EINVAL;
859708d9893SSyed Nayyar Waris 	}
860954ab5ccSWilliam Breathitt Gray 
861954ab5ccSWilliam Breathitt Gray 	/* Logic 0 = cable fault */
862954ab5ccSWilliam Breathitt Gray 	status = inb(priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
863954ab5ccSWilliam Breathitt Gray 
86409db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
865708d9893SSyed Nayyar Waris 
866954ab5ccSWilliam Breathitt Gray 	/* Mask respective channel and invert logic */
867aaec1a0fSWilliam Breathitt Gray 	*cable_fault = !(status & BIT(channel_id));
868954ab5ccSWilliam Breathitt Gray 
869aaec1a0fSWilliam Breathitt Gray 	return 0;
870954ab5ccSWilliam Breathitt Gray }
871954ab5ccSWilliam Breathitt Gray 
872aaec1a0fSWilliam Breathitt Gray static int quad8_signal_cable_fault_enable_read(struct counter_device *counter,
873aaec1a0fSWilliam Breathitt Gray 						struct counter_signal *signal,
874aaec1a0fSWilliam Breathitt Gray 						u8 *enable)
875954ab5ccSWilliam Breathitt Gray {
876*aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
877954ab5ccSWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
878954ab5ccSWilliam Breathitt Gray 
879aaec1a0fSWilliam Breathitt Gray 	*enable = !!(priv->cable_fault_enable & BIT(channel_id));
880aaec1a0fSWilliam Breathitt Gray 
881aaec1a0fSWilliam Breathitt Gray 	return 0;
882954ab5ccSWilliam Breathitt Gray }
883954ab5ccSWilliam Breathitt Gray 
884aaec1a0fSWilliam Breathitt Gray static int quad8_signal_cable_fault_enable_write(struct counter_device *counter,
885aaec1a0fSWilliam Breathitt Gray 						 struct counter_signal *signal,
886aaec1a0fSWilliam Breathitt Gray 						 u8 enable)
887954ab5ccSWilliam Breathitt Gray {
888*aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
889954ab5ccSWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
89009db4678SWilliam Breathitt Gray 	unsigned long irqflags;
891954ab5ccSWilliam Breathitt Gray 	unsigned int cable_fault_enable;
892954ab5ccSWilliam Breathitt Gray 
89309db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
894708d9893SSyed Nayyar Waris 
895954ab5ccSWilliam Breathitt Gray 	if (enable)
896954ab5ccSWilliam Breathitt Gray 		priv->cable_fault_enable |= BIT(channel_id);
897954ab5ccSWilliam Breathitt Gray 	else
898954ab5ccSWilliam Breathitt Gray 		priv->cable_fault_enable &= ~BIT(channel_id);
899954ab5ccSWilliam Breathitt Gray 
900954ab5ccSWilliam Breathitt Gray 	/* Enable is active low in Differential Encoder Cable Status register */
901954ab5ccSWilliam Breathitt Gray 	cable_fault_enable = ~priv->cable_fault_enable;
902954ab5ccSWilliam Breathitt Gray 
903954ab5ccSWilliam Breathitt Gray 	outb(cable_fault_enable, priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
904954ab5ccSWilliam Breathitt Gray 
90509db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
906708d9893SSyed Nayyar Waris 
907aaec1a0fSWilliam Breathitt Gray 	return 0;
908954ab5ccSWilliam Breathitt Gray }
909954ab5ccSWilliam Breathitt Gray 
910aaec1a0fSWilliam Breathitt Gray static int quad8_signal_fck_prescaler_read(struct counter_device *counter,
911aaec1a0fSWilliam Breathitt Gray 					   struct counter_signal *signal,
912aaec1a0fSWilliam Breathitt Gray 					   u8 *prescaler)
913de65d055SWilliam Breathitt Gray {
914*aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
915de65d055SWilliam Breathitt Gray 
916aaec1a0fSWilliam Breathitt Gray 	*prescaler = priv->fck_prescaler[signal->id / 2];
917aaec1a0fSWilliam Breathitt Gray 
918aaec1a0fSWilliam Breathitt Gray 	return 0;
919de65d055SWilliam Breathitt Gray }
920de65d055SWilliam Breathitt Gray 
921aaec1a0fSWilliam Breathitt Gray static int quad8_signal_fck_prescaler_write(struct counter_device *counter,
922aaec1a0fSWilliam Breathitt Gray 					    struct counter_signal *signal,
923aaec1a0fSWilliam Breathitt Gray 					    u8 prescaler)
924de65d055SWilliam Breathitt Gray {
925*aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
926de65d055SWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
927de65d055SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * channel_id;
92809db4678SWilliam Breathitt Gray 	unsigned long irqflags;
929de65d055SWilliam Breathitt Gray 
93009db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
931d5ed76adSSyed Nayyar Waris 
932de65d055SWilliam Breathitt Gray 	priv->fck_prescaler[channel_id] = prescaler;
933de65d055SWilliam Breathitt Gray 
934de65d055SWilliam Breathitt Gray 	/* Reset Byte Pointer */
935de65d055SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
936de65d055SWilliam Breathitt Gray 
937de65d055SWilliam Breathitt Gray 	/* Set filter clock factor */
938de65d055SWilliam Breathitt Gray 	outb(prescaler, base_offset);
939de65d055SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
940de65d055SWilliam Breathitt Gray 	     base_offset + 1);
941de65d055SWilliam Breathitt Gray 
94209db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
943d5ed76adSSyed Nayyar Waris 
944aaec1a0fSWilliam Breathitt Gray 	return 0;
945de65d055SWilliam Breathitt Gray }
946de65d055SWilliam Breathitt Gray 
947aaec1a0fSWilliam Breathitt Gray static struct counter_comp quad8_signal_ext[] = {
948aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_BOOL("cable_fault", quad8_signal_cable_fault_read,
949aaec1a0fSWilliam Breathitt Gray 				 NULL),
950aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_BOOL("cable_fault_enable",
951aaec1a0fSWilliam Breathitt Gray 				 quad8_signal_cable_fault_enable_read,
952aaec1a0fSWilliam Breathitt Gray 				 quad8_signal_cable_fault_enable_write),
953aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_U8("filter_clock_prescaler",
954aaec1a0fSWilliam Breathitt Gray 			       quad8_signal_fck_prescaler_read,
955aaec1a0fSWilliam Breathitt Gray 			       quad8_signal_fck_prescaler_write)
956de65d055SWilliam Breathitt Gray };
957de65d055SWilliam Breathitt Gray 
958aaec1a0fSWilliam Breathitt Gray static DEFINE_COUNTER_ENUM(quad8_index_pol_enum, quad8_index_polarity_modes);
959aaec1a0fSWilliam Breathitt Gray static DEFINE_COUNTER_ENUM(quad8_synch_mode_enum, quad8_synchronous_modes);
960aaec1a0fSWilliam Breathitt Gray 
961aaec1a0fSWilliam Breathitt Gray static struct counter_comp quad8_index_ext[] = {
962aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_ENUM("index_polarity", quad8_index_polarity_get,
963aaec1a0fSWilliam Breathitt Gray 				 quad8_index_polarity_set,
964aaec1a0fSWilliam Breathitt Gray 				 quad8_index_pol_enum),
965aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_ENUM("synchronous_mode", quad8_synchronous_mode_get,
966aaec1a0fSWilliam Breathitt Gray 				 quad8_synchronous_mode_set,
967aaec1a0fSWilliam Breathitt Gray 				 quad8_synch_mode_enum),
968f1d8a071SWilliam Breathitt Gray };
969f1d8a071SWilliam Breathitt Gray 
970f1d8a071SWilliam Breathitt Gray #define QUAD8_QUAD_SIGNAL(_id, _name) {		\
971f1d8a071SWilliam Breathitt Gray 	.id = (_id),				\
972de65d055SWilliam Breathitt Gray 	.name = (_name),			\
973de65d055SWilliam Breathitt Gray 	.ext = quad8_signal_ext,		\
974de65d055SWilliam Breathitt Gray 	.num_ext = ARRAY_SIZE(quad8_signal_ext)	\
975f1d8a071SWilliam Breathitt Gray }
976f1d8a071SWilliam Breathitt Gray 
977f1d8a071SWilliam Breathitt Gray #define	QUAD8_INDEX_SIGNAL(_id, _name) {	\
978f1d8a071SWilliam Breathitt Gray 	.id = (_id),				\
979f1d8a071SWilliam Breathitt Gray 	.name = (_name),			\
980f1d8a071SWilliam Breathitt Gray 	.ext = quad8_index_ext,			\
981f1d8a071SWilliam Breathitt Gray 	.num_ext = ARRAY_SIZE(quad8_index_ext)	\
982f1d8a071SWilliam Breathitt Gray }
983f1d8a071SWilliam Breathitt Gray 
984f1d8a071SWilliam Breathitt Gray static struct counter_signal quad8_signals[] = {
985f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(0, "Channel 1 Quadrature A"),
986f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(1, "Channel 1 Quadrature B"),
987f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(2, "Channel 2 Quadrature A"),
988f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(3, "Channel 2 Quadrature B"),
989f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(4, "Channel 3 Quadrature A"),
990f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(5, "Channel 3 Quadrature B"),
991f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(6, "Channel 4 Quadrature A"),
992f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(7, "Channel 4 Quadrature B"),
993f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(8, "Channel 5 Quadrature A"),
994f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(9, "Channel 5 Quadrature B"),
995f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(10, "Channel 6 Quadrature A"),
996f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(11, "Channel 6 Quadrature B"),
997f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(12, "Channel 7 Quadrature A"),
998f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(13, "Channel 7 Quadrature B"),
999f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(14, "Channel 8 Quadrature A"),
1000f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(15, "Channel 8 Quadrature B"),
1001f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(16, "Channel 1 Index"),
1002f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(17, "Channel 2 Index"),
1003f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(18, "Channel 3 Index"),
1004f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(19, "Channel 4 Index"),
1005f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(20, "Channel 5 Index"),
1006f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(21, "Channel 6 Index"),
1007f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(22, "Channel 7 Index"),
1008f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(23, "Channel 8 Index")
1009f1d8a071SWilliam Breathitt Gray };
1010f1d8a071SWilliam Breathitt Gray 
1011f1d8a071SWilliam Breathitt Gray #define QUAD8_COUNT_SYNAPSES(_id) {					\
1012f1d8a071SWilliam Breathitt Gray 	{								\
1013f1d8a071SWilliam Breathitt Gray 		.actions_list = quad8_synapse_actions_list,		\
1014f1d8a071SWilliam Breathitt Gray 		.num_actions = ARRAY_SIZE(quad8_synapse_actions_list),	\
1015f1d8a071SWilliam Breathitt Gray 		.signal = quad8_signals + 2 * (_id)			\
1016f1d8a071SWilliam Breathitt Gray 	},								\
1017f1d8a071SWilliam Breathitt Gray 	{								\
1018f1d8a071SWilliam Breathitt Gray 		.actions_list = quad8_synapse_actions_list,		\
1019f1d8a071SWilliam Breathitt Gray 		.num_actions = ARRAY_SIZE(quad8_synapse_actions_list),	\
1020f1d8a071SWilliam Breathitt Gray 		.signal = quad8_signals + 2 * (_id) + 1			\
1021f1d8a071SWilliam Breathitt Gray 	},								\
1022f1d8a071SWilliam Breathitt Gray 	{								\
1023f1d8a071SWilliam Breathitt Gray 		.actions_list = quad8_index_actions_list,		\
1024f1d8a071SWilliam Breathitt Gray 		.num_actions = ARRAY_SIZE(quad8_index_actions_list),	\
1025f1d8a071SWilliam Breathitt Gray 		.signal = quad8_signals + 2 * (_id) + 16		\
1026f1d8a071SWilliam Breathitt Gray 	}								\
1027f1d8a071SWilliam Breathitt Gray }
1028f1d8a071SWilliam Breathitt Gray 
1029f1d8a071SWilliam Breathitt Gray static struct counter_synapse quad8_count_synapses[][3] = {
1030f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(0), QUAD8_COUNT_SYNAPSES(1),
1031f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(2), QUAD8_COUNT_SYNAPSES(3),
1032f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(4), QUAD8_COUNT_SYNAPSES(5),
1033f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(6), QUAD8_COUNT_SYNAPSES(7)
1034f1d8a071SWilliam Breathitt Gray };
1035f1d8a071SWilliam Breathitt Gray 
1036aaec1a0fSWilliam Breathitt Gray static const enum counter_count_mode quad8_cnt_modes[] = {
1037aaec1a0fSWilliam Breathitt Gray 	COUNTER_COUNT_MODE_NORMAL,
1038aaec1a0fSWilliam Breathitt Gray 	COUNTER_COUNT_MODE_RANGE_LIMIT,
1039aaec1a0fSWilliam Breathitt Gray 	COUNTER_COUNT_MODE_NON_RECYCLE,
1040aaec1a0fSWilliam Breathitt Gray 	COUNTER_COUNT_MODE_MODULO_N,
1041aaec1a0fSWilliam Breathitt Gray };
1042aaec1a0fSWilliam Breathitt Gray 
1043aaec1a0fSWilliam Breathitt Gray static DEFINE_COUNTER_AVAILABLE(quad8_count_mode_available, quad8_cnt_modes);
1044aaec1a0fSWilliam Breathitt Gray 
1045aaec1a0fSWilliam Breathitt Gray static DEFINE_COUNTER_ENUM(quad8_error_noise_enum, quad8_noise_error_states);
1046aaec1a0fSWilliam Breathitt Gray 
1047aaec1a0fSWilliam Breathitt Gray static struct counter_comp quad8_count_ext[] = {
1048aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_CEILING(quad8_count_ceiling_read,
1049aaec1a0fSWilliam Breathitt Gray 			     quad8_count_ceiling_write),
1050aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_FLOOR(quad8_count_floor_read, NULL),
1051aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_COUNT_MODE(quad8_count_mode_read, quad8_count_mode_write,
1052aaec1a0fSWilliam Breathitt Gray 				quad8_count_mode_available),
1053aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_DIRECTION(quad8_direction_read),
1054aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_ENABLE(quad8_count_enable_read, quad8_count_enable_write),
1055aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_COUNT_ENUM("error_noise", quad8_error_noise_get, NULL,
1056aaec1a0fSWilliam Breathitt Gray 				quad8_error_noise_enum),
1057aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_PRESET(quad8_count_preset_read, quad8_count_preset_write),
1058aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_PRESET_ENABLE(quad8_count_preset_enable_read,
1059aaec1a0fSWilliam Breathitt Gray 				   quad8_count_preset_enable_write),
1060f1d8a071SWilliam Breathitt Gray };
1061f1d8a071SWilliam Breathitt Gray 
1062f1d8a071SWilliam Breathitt Gray #define QUAD8_COUNT(_id, _cntname) {					\
1063f1d8a071SWilliam Breathitt Gray 	.id = (_id),							\
1064f1d8a071SWilliam Breathitt Gray 	.name = (_cntname),						\
1065f1d8a071SWilliam Breathitt Gray 	.functions_list = quad8_count_functions_list,			\
1066f1d8a071SWilliam Breathitt Gray 	.num_functions = ARRAY_SIZE(quad8_count_functions_list),	\
1067f1d8a071SWilliam Breathitt Gray 	.synapses = quad8_count_synapses[(_id)],			\
1068f1d8a071SWilliam Breathitt Gray 	.num_synapses =	2,						\
1069f1d8a071SWilliam Breathitt Gray 	.ext = quad8_count_ext,						\
1070f1d8a071SWilliam Breathitt Gray 	.num_ext = ARRAY_SIZE(quad8_count_ext)				\
1071f1d8a071SWilliam Breathitt Gray }
1072f1d8a071SWilliam Breathitt Gray 
1073f1d8a071SWilliam Breathitt Gray static struct counter_count quad8_counts[] = {
1074f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(0, "Channel 1 Count"),
1075f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(1, "Channel 2 Count"),
1076f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(2, "Channel 3 Count"),
1077f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(3, "Channel 4 Count"),
1078f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(4, "Channel 5 Count"),
1079f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(5, "Channel 6 Count"),
1080f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(6, "Channel 7 Count"),
1081f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(7, "Channel 8 Count")
1082f1d8a071SWilliam Breathitt Gray };
1083f1d8a071SWilliam Breathitt Gray 
10847aa2ba0dSWilliam Breathitt Gray static irqreturn_t quad8_irq_handler(int irq, void *private)
10857aa2ba0dSWilliam Breathitt Gray {
10867aa2ba0dSWilliam Breathitt Gray 	struct quad8 *const priv = private;
10877aa2ba0dSWilliam Breathitt Gray 	const unsigned long base = priv->base;
10887aa2ba0dSWilliam Breathitt Gray 	unsigned long irq_status;
10897aa2ba0dSWilliam Breathitt Gray 	unsigned long channel;
10907aa2ba0dSWilliam Breathitt Gray 	u8 event;
10917aa2ba0dSWilliam Breathitt Gray 
10927aa2ba0dSWilliam Breathitt Gray 	irq_status = inb(base + QUAD8_REG_INTERRUPT_STATUS);
10937aa2ba0dSWilliam Breathitt Gray 	if (!irq_status)
10947aa2ba0dSWilliam Breathitt Gray 		return IRQ_NONE;
10957aa2ba0dSWilliam Breathitt Gray 
10967aa2ba0dSWilliam Breathitt Gray 	for_each_set_bit(channel, &irq_status, QUAD8_NUM_COUNTERS) {
10977aa2ba0dSWilliam Breathitt Gray 		switch (priv->irq_trigger[channel]) {
10987aa2ba0dSWilliam Breathitt Gray 		case QUAD8_EVENT_CARRY:
10997aa2ba0dSWilliam Breathitt Gray 			event = COUNTER_EVENT_OVERFLOW;
11007aa2ba0dSWilliam Breathitt Gray 				break;
11017aa2ba0dSWilliam Breathitt Gray 		case QUAD8_EVENT_COMPARE:
11027aa2ba0dSWilliam Breathitt Gray 			event = COUNTER_EVENT_THRESHOLD;
11037aa2ba0dSWilliam Breathitt Gray 				break;
11047aa2ba0dSWilliam Breathitt Gray 		case QUAD8_EVENT_CARRY_BORROW:
11057aa2ba0dSWilliam Breathitt Gray 			event = COUNTER_EVENT_OVERFLOW_UNDERFLOW;
11067aa2ba0dSWilliam Breathitt Gray 				break;
11077aa2ba0dSWilliam Breathitt Gray 		case QUAD8_EVENT_INDEX:
11087aa2ba0dSWilliam Breathitt Gray 			event = COUNTER_EVENT_INDEX;
11097aa2ba0dSWilliam Breathitt Gray 				break;
11107aa2ba0dSWilliam Breathitt Gray 		default:
11117aa2ba0dSWilliam Breathitt Gray 			/* should never reach this path */
11127aa2ba0dSWilliam Breathitt Gray 			WARN_ONCE(true, "invalid interrupt trigger function %u configured for channel %lu\n",
11137aa2ba0dSWilliam Breathitt Gray 				  priv->irq_trigger[channel], channel);
11147aa2ba0dSWilliam Breathitt Gray 			continue;
11157aa2ba0dSWilliam Breathitt Gray 		}
11167aa2ba0dSWilliam Breathitt Gray 
11177aa2ba0dSWilliam Breathitt Gray 		counter_push_event(&priv->counter, event, channel);
11187aa2ba0dSWilliam Breathitt Gray 	}
11197aa2ba0dSWilliam Breathitt Gray 
11207aa2ba0dSWilliam Breathitt Gray 	/* Clear pending interrupts on device */
11217aa2ba0dSWilliam Breathitt Gray 	outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base + QUAD8_REG_CHAN_OP);
11227aa2ba0dSWilliam Breathitt Gray 
11237aa2ba0dSWilliam Breathitt Gray 	return IRQ_HANDLED;
11247aa2ba0dSWilliam Breathitt Gray }
11257aa2ba0dSWilliam Breathitt Gray 
1126f1d8a071SWilliam Breathitt Gray static int quad8_probe(struct device *dev, unsigned int id)
1127f1d8a071SWilliam Breathitt Gray {
1128e357e81fSWilliam Breathitt Gray 	struct quad8 *priv;
1129f1d8a071SWilliam Breathitt Gray 	int i, j;
1130f1d8a071SWilliam Breathitt Gray 	unsigned int base_offset;
11317aa2ba0dSWilliam Breathitt Gray 	int err;
1132f1d8a071SWilliam Breathitt Gray 
1133f1d8a071SWilliam Breathitt Gray 	if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) {
1134f1d8a071SWilliam Breathitt Gray 		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
1135f1d8a071SWilliam Breathitt Gray 			base[id], base[id] + QUAD8_EXTENT);
1136f1d8a071SWilliam Breathitt Gray 		return -EBUSY;
1137f1d8a071SWilliam Breathitt Gray 	}
1138f1d8a071SWilliam Breathitt Gray 
1139e357e81fSWilliam Breathitt Gray 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
1140e357e81fSWilliam Breathitt Gray 	if (!priv)
1141f1d8a071SWilliam Breathitt Gray 		return -ENOMEM;
1142f1d8a071SWilliam Breathitt Gray 
1143f1d8a071SWilliam Breathitt Gray 	/* Initialize Counter device and driver data */
1144e357e81fSWilliam Breathitt Gray 	priv->counter.name = dev_name(dev);
1145e357e81fSWilliam Breathitt Gray 	priv->counter.parent = dev;
1146e357e81fSWilliam Breathitt Gray 	priv->counter.ops = &quad8_ops;
1147e357e81fSWilliam Breathitt Gray 	priv->counter.counts = quad8_counts;
1148e357e81fSWilliam Breathitt Gray 	priv->counter.num_counts = ARRAY_SIZE(quad8_counts);
1149e357e81fSWilliam Breathitt Gray 	priv->counter.signals = quad8_signals;
1150e357e81fSWilliam Breathitt Gray 	priv->counter.num_signals = ARRAY_SIZE(quad8_signals);
1151e357e81fSWilliam Breathitt Gray 	priv->counter.priv = priv;
1152e357e81fSWilliam Breathitt Gray 	priv->base = base[id];
1153f1d8a071SWilliam Breathitt Gray 
115409db4678SWilliam Breathitt Gray 	spin_lock_init(&priv->lock);
1155fc069262SSyed Nayyar Waris 
11567aa2ba0dSWilliam Breathitt Gray 	/* Reset Index/Interrupt Register */
11577aa2ba0dSWilliam Breathitt Gray 	outb(0x00, base[id] + QUAD8_REG_INDEX_INTERRUPT);
1158f1d8a071SWilliam Breathitt Gray 	/* Reset all counters and disable interrupt function */
1159f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
1160f1d8a071SWilliam Breathitt Gray 	/* Set initial configuration for all counters */
1161f1d8a071SWilliam Breathitt Gray 	for (i = 0; i < QUAD8_NUM_COUNTERS; i++) {
1162f1d8a071SWilliam Breathitt Gray 		base_offset = base[id] + 2 * i;
1163f1d8a071SWilliam Breathitt Gray 		/* Reset Byte Pointer */
1164f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
1165de65d055SWilliam Breathitt Gray 		/* Reset filter clock factor */
1166de65d055SWilliam Breathitt Gray 		outb(0, base_offset);
1167de65d055SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
1168de65d055SWilliam Breathitt Gray 		     base_offset + 1);
1169de65d055SWilliam Breathitt Gray 		/* Reset Byte Pointer */
1170de65d055SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
1171f1d8a071SWilliam Breathitt Gray 		/* Reset Preset Register */
1172f1d8a071SWilliam Breathitt Gray 		for (j = 0; j < 3; j++)
1173f1d8a071SWilliam Breathitt Gray 			outb(0x00, base_offset);
1174f1d8a071SWilliam Breathitt Gray 		/* Reset Borrow, Carry, Compare, and Sign flags */
1175f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
1176f1d8a071SWilliam Breathitt Gray 		/* Reset Error flag */
1177f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
1178f1d8a071SWilliam Breathitt Gray 		/* Binary encoding; Normal count; non-quadrature mode */
1179f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_CMR, base_offset + 1);
1180f1d8a071SWilliam Breathitt Gray 		/* Disable A and B inputs; preset on index; FLG1 as Carry */
1181f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_IOR, base_offset + 1);
1182f1d8a071SWilliam Breathitt Gray 		/* Disable index function; negative index polarity */
1183f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_IDR, base_offset + 1);
1184f1d8a071SWilliam Breathitt Gray 	}
1185954ab5ccSWilliam Breathitt Gray 	/* Disable Differential Encoder Cable Status for all channels */
1186954ab5ccSWilliam Breathitt Gray 	outb(0xFF, base[id] + QUAD8_DIFF_ENCODER_CABLE_STATUS);
11877aa2ba0dSWilliam Breathitt Gray 	/* Enable all counters and enable interrupt function */
11887aa2ba0dSWilliam Breathitt Gray 	outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base[id] + QUAD8_REG_CHAN_OP);
11897aa2ba0dSWilliam Breathitt Gray 
11907aa2ba0dSWilliam Breathitt Gray 	err = devm_request_irq(dev, irq[id], quad8_irq_handler, IRQF_SHARED,
11917aa2ba0dSWilliam Breathitt Gray 			       priv->counter.name, priv);
11927aa2ba0dSWilliam Breathitt Gray 	if (err)
11937aa2ba0dSWilliam Breathitt Gray 		return err;
1194f1d8a071SWilliam Breathitt Gray 
1195e357e81fSWilliam Breathitt Gray 	return devm_counter_register(dev, &priv->counter);
1196f1d8a071SWilliam Breathitt Gray }
1197f1d8a071SWilliam Breathitt Gray 
1198f1d8a071SWilliam Breathitt Gray static struct isa_driver quad8_driver = {
1199f1d8a071SWilliam Breathitt Gray 	.probe = quad8_probe,
1200f1d8a071SWilliam Breathitt Gray 	.driver = {
1201f1d8a071SWilliam Breathitt Gray 		.name = "104-quad-8"
1202f1d8a071SWilliam Breathitt Gray 	}
1203f1d8a071SWilliam Breathitt Gray };
1204f1d8a071SWilliam Breathitt Gray 
1205f1d8a071SWilliam Breathitt Gray module_isa_driver(quad8_driver, num_quad8);
1206f1d8a071SWilliam Breathitt Gray 
1207f1d8a071SWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
1208e357e81fSWilliam Breathitt Gray MODULE_DESCRIPTION("ACCES 104-QUAD-8 driver");
1209f1d8a071SWilliam Breathitt Gray MODULE_LICENSE("GPL v2");
1210