blob: 1cbd60aaed6970012d958f034bb648e91d0b0210 [file] [log] [blame]
William Breathitt Grayf1d8a072019-04-02 15:30:40 +09001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Counter driver for the ACCES 104-QUAD-8
4 * Copyright (C) 2016 William Breathitt Gray
5 *
6 * This driver supports the ACCES 104-QUAD-8 and ACCES 104-QUAD-4.
7 */
8#include <linux/bitops.h>
9#include <linux/counter.h>
10#include <linux/device.h>
11#include <linux/errno.h>
William Breathitt Grayf1d8a072019-04-02 15:30:40 +090012#include <linux/io.h>
13#include <linux/ioport.h>
William Breathitt Gray7aa2ba02021-09-29 12:16:06 +090014#include <linux/interrupt.h>
William Breathitt Grayf1d8a072019-04-02 15:30:40 +090015#include <linux/isa.h>
16#include <linux/kernel.h>
17#include <linux/module.h>
18#include <linux/moduleparam.h>
19#include <linux/types.h>
William Breathitt Gray09db4672021-09-29 12:16:05 +090020#include <linux/spinlock.h>
William Breathitt Grayf1d8a072019-04-02 15:30:40 +090021
22#define QUAD8_EXTENT 32
23
24static unsigned int base[max_num_isa_dev(QUAD8_EXTENT)];
25static unsigned int num_quad8;
William Breathitt Grayaf383bb12021-06-09 10:31:08 +090026module_param_hw_array(base, uint, ioport, &num_quad8, 0);
William Breathitt Grayf1d8a072019-04-02 15:30:40 +090027MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
28
William Breathitt Gray7aa2ba02021-09-29 12:16:06 +090029static unsigned int irq[max_num_isa_dev(QUAD8_EXTENT)];
30module_param_hw_array(irq, uint, irq, NULL, 0);
31MODULE_PARM_DESC(irq, "ACCES 104-QUAD-8 interrupt line numbers");
32
William Breathitt Grayf1d8a072019-04-02 15:30:40 +090033#define QUAD8_NUM_COUNTERS 8
34
35/**
William Breathitt Graye357e812021-01-30 11:37:03 +090036 * struct quad8 - device private data structure
William Breathitt Gray94a853e2021-08-09 21:37:26 +090037 * @lock: lock to prevent clobbering device states during R/W ops
William Breathitt Grayf1d8a072019-04-02 15:30:40 +090038 * @counter: instance of the counter_device
William Breathitt Gray954ab5c2020-03-01 17:07:19 -050039 * @fck_prescaler: array of filter clock prescaler configurations
William Breathitt Grayf1d8a072019-04-02 15:30:40 +090040 * @preset: array of preset values
41 * @count_mode: array of count mode configurations
42 * @quadrature_mode: array of quadrature mode configurations
43 * @quadrature_scale: array of quadrature mode scale configurations
44 * @ab_enable: array of A and B inputs enable configurations
45 * @preset_enable: array of set_to_preset_on_index attribute configurations
William Breathitt Gray7aa2ba02021-09-29 12:16:06 +090046 * @irq_trigger: array of current IRQ trigger function configurations
47 * @next_irq_trigger: array of next IRQ trigger function configurations
William Breathitt Grayf1d8a072019-04-02 15:30:40 +090048 * @synchronous_mode: array of index function synchronous mode configurations
49 * @index_polarity: array of index function polarity configurations
William Breathitt Gray954ab5c2020-03-01 17:07:19 -050050 * @cable_fault_enable: differential encoder cable status enable configurations
William Breathitt Graye357e812021-01-30 11:37:03 +090051 * @base: base port address of the device
William Breathitt Grayf1d8a072019-04-02 15:30:40 +090052 */
William Breathitt Graye357e812021-01-30 11:37:03 +090053struct quad8 {
William Breathitt Gray09db4672021-09-29 12:16:05 +090054 spinlock_t lock;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +090055 struct counter_device counter;
William Breathitt Grayde65d052020-02-22 11:49:58 -050056 unsigned int fck_prescaler[QUAD8_NUM_COUNTERS];
William Breathitt Grayf1d8a072019-04-02 15:30:40 +090057 unsigned int preset[QUAD8_NUM_COUNTERS];
58 unsigned int count_mode[QUAD8_NUM_COUNTERS];
59 unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
60 unsigned int quadrature_scale[QUAD8_NUM_COUNTERS];
61 unsigned int ab_enable[QUAD8_NUM_COUNTERS];
62 unsigned int preset_enable[QUAD8_NUM_COUNTERS];
William Breathitt Gray7aa2ba02021-09-29 12:16:06 +090063 unsigned int irq_trigger[QUAD8_NUM_COUNTERS];
64 unsigned int next_irq_trigger[QUAD8_NUM_COUNTERS];
William Breathitt Grayf1d8a072019-04-02 15:30:40 +090065 unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
66 unsigned int index_polarity[QUAD8_NUM_COUNTERS];
William Breathitt Gray954ab5c2020-03-01 17:07:19 -050067 unsigned int cable_fault_enable;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +090068 unsigned int base;
69};
70
William Breathitt Gray7aa2ba02021-09-29 12:16:06 +090071#define QUAD8_REG_INTERRUPT_STATUS 0x10
William Breathitt Grayf1d8a072019-04-02 15:30:40 +090072#define QUAD8_REG_CHAN_OP 0x11
William Breathitt Gray7aa2ba02021-09-29 12:16:06 +090073#define QUAD8_REG_INDEX_INTERRUPT 0x12
William Breathitt Grayf1d8a072019-04-02 15:30:40 +090074#define QUAD8_REG_INDEX_INPUT_LEVELS 0x16
William Breathitt Gray954ab5c2020-03-01 17:07:19 -050075#define QUAD8_DIFF_ENCODER_CABLE_STATUS 0x17
William Breathitt Grayf1d8a072019-04-02 15:30:40 +090076/* Borrow Toggle flip-flop */
77#define QUAD8_FLAG_BT BIT(0)
78/* Carry Toggle flip-flop */
79#define QUAD8_FLAG_CT BIT(1)
80/* Error flag */
81#define QUAD8_FLAG_E BIT(4)
82/* Up/Down flag */
83#define QUAD8_FLAG_UD BIT(5)
84/* Reset and Load Signal Decoders */
85#define QUAD8_CTR_RLD 0x00
86/* Counter Mode Register */
87#define QUAD8_CTR_CMR 0x20
88/* Input / Output Control Register */
89#define QUAD8_CTR_IOR 0x40
90/* Index Control Register */
91#define QUAD8_CTR_IDR 0x60
92/* Reset Byte Pointer (three byte data pointer) */
93#define QUAD8_RLD_RESET_BP 0x01
94/* Reset Counter */
95#define QUAD8_RLD_RESET_CNTR 0x02
96/* Reset Borrow Toggle, Carry Toggle, Compare Toggle, and Sign flags */
97#define QUAD8_RLD_RESET_FLAGS 0x04
98/* Reset Error flag */
99#define QUAD8_RLD_RESET_E 0x06
100/* Preset Register to Counter */
101#define QUAD8_RLD_PRESET_CNTR 0x08
102/* Transfer Counter to Output Latch */
103#define QUAD8_RLD_CNTR_OUT 0x10
William Breathitt Grayde65d052020-02-22 11:49:58 -0500104/* Transfer Preset Register LSB to FCK Prescaler */
105#define QUAD8_RLD_PRESET_PSC 0x18
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900106#define QUAD8_CHAN_OP_RESET_COUNTERS 0x01
William Breathitt Gray7aa2ba02021-09-29 12:16:06 +0900107#define QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC 0x04
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900108#define QUAD8_CMR_QUADRATURE_X1 0x08
109#define QUAD8_CMR_QUADRATURE_X2 0x10
110#define QUAD8_CMR_QUADRATURE_X4 0x18
111
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900112static int quad8_signal_read(struct counter_device *counter,
William Breathitt Gray493b9382021-08-03 21:06:14 +0900113 struct counter_signal *signal,
114 enum counter_signal_level *level)
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900115{
William Breathitt Graye357e812021-01-30 11:37:03 +0900116 const struct quad8 *const priv = counter->priv;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900117 unsigned int state;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900118
119 /* Only Index signal levels can be read */
120 if (signal->id < 16)
121 return -EINVAL;
122
123 state = inb(priv->base + QUAD8_REG_INDEX_INPUT_LEVELS)
124 & BIT(signal->id - 16);
125
William Breathitt Gray493b9382021-08-03 21:06:14 +0900126 *level = (state) ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900127
128 return 0;
129}
130
131static int quad8_count_read(struct counter_device *counter,
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900132 struct counter_count *count, u64 *val)
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900133{
William Breathitt Graye357e812021-01-30 11:37:03 +0900134 struct quad8 *const priv = counter->priv;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900135 const int base_offset = priv->base + 2 * count->id;
136 unsigned int flags;
137 unsigned int borrow;
138 unsigned int carry;
William Breathitt Gray09db4672021-09-29 12:16:05 +0900139 unsigned long irqflags;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900140 int i;
141
142 flags = inb(base_offset + 1);
143 borrow = flags & QUAD8_FLAG_BT;
144 carry = !!(flags & QUAD8_FLAG_CT);
145
146 /* Borrow XOR Carry effectively doubles count range */
William Breathitt Grayd49e6ee2019-10-06 16:03:09 -0400147 *val = (unsigned long)(borrow ^ carry) << 24;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900148
William Breathitt Gray09db4672021-09-29 12:16:05 +0900149 spin_lock_irqsave(&priv->lock, irqflags);
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530150
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900151 /* Reset Byte Pointer; transfer Counter to Output Latch */
152 outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
153 base_offset + 1);
154
155 for (i = 0; i < 3; i++)
William Breathitt Grayd49e6ee2019-10-06 16:03:09 -0400156 *val |= (unsigned long)inb(base_offset) << (8 * i);
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900157
William Breathitt Gray09db4672021-09-29 12:16:05 +0900158 spin_unlock_irqrestore(&priv->lock, irqflags);
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530159
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900160 return 0;
161}
162
163static int quad8_count_write(struct counter_device *counter,
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900164 struct counter_count *count, u64 val)
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900165{
William Breathitt Graye357e812021-01-30 11:37:03 +0900166 struct quad8 *const priv = counter->priv;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900167 const int base_offset = priv->base + 2 * count->id;
William Breathitt Gray09db4672021-09-29 12:16:05 +0900168 unsigned long irqflags;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900169 int i;
170
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900171 /* Only 24-bit values are supported */
William Breathitt Grayd49e6ee2019-10-06 16:03:09 -0400172 if (val > 0xFFFFFF)
William Breathitt Graye2ff3192021-08-03 21:06:13 +0900173 return -ERANGE;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900174
William Breathitt Gray09db4672021-09-29 12:16:05 +0900175 spin_lock_irqsave(&priv->lock, irqflags);
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530176
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900177 /* Reset Byte Pointer */
178 outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
179
180 /* Counter can only be set via Preset Register */
181 for (i = 0; i < 3; i++)
William Breathitt Grayd49e6ee2019-10-06 16:03:09 -0400182 outb(val >> (8 * i), base_offset);
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900183
184 /* Transfer Preset Register to Counter */
185 outb(QUAD8_CTR_RLD | QUAD8_RLD_PRESET_CNTR, base_offset + 1);
186
187 /* Reset Byte Pointer */
188 outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
189
190 /* Set Preset Register back to original value */
William Breathitt Grayd49e6ee2019-10-06 16:03:09 -0400191 val = priv->preset[count->id];
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900192 for (i = 0; i < 3; i++)
William Breathitt Grayd49e6ee2019-10-06 16:03:09 -0400193 outb(val >> (8 * i), base_offset);
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900194
195 /* Reset Borrow, Carry, Compare, and Sign flags */
196 outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
197 /* Reset Error flag */
198 outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
199
William Breathitt Gray09db4672021-09-29 12:16:05 +0900200 spin_unlock_irqrestore(&priv->lock, irqflags);
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530201
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900202 return 0;
203}
204
William Breathitt Gray394a0152021-08-03 21:06:15 +0900205static const enum counter_function quad8_count_functions_list[] = {
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900206 COUNTER_FUNCTION_PULSE_DIRECTION,
207 COUNTER_FUNCTION_QUADRATURE_X1_A,
208 COUNTER_FUNCTION_QUADRATURE_X2_A,
209 COUNTER_FUNCTION_QUADRATURE_X4,
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900210};
211
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900212static int quad8_function_read(struct counter_device *counter,
213 struct counter_count *count,
214 enum counter_function *function)
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900215{
William Breathitt Graye357e812021-01-30 11:37:03 +0900216 struct quad8 *const priv = counter->priv;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900217 const int id = count->id;
William Breathitt Gray09db4672021-09-29 12:16:05 +0900218 unsigned long irqflags;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900219
William Breathitt Gray09db4672021-09-29 12:16:05 +0900220 spin_lock_irqsave(&priv->lock, irqflags);
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530221
222 if (priv->quadrature_mode[id])
223 switch (priv->quadrature_scale[id]) {
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900224 case 0:
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900225 *function = COUNTER_FUNCTION_QUADRATURE_X1_A;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900226 break;
227 case 1:
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900228 *function = COUNTER_FUNCTION_QUADRATURE_X2_A;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900229 break;
230 case 2:
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900231 *function = COUNTER_FUNCTION_QUADRATURE_X4;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900232 break;
233 }
234 else
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900235 *function = COUNTER_FUNCTION_PULSE_DIRECTION;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900236
William Breathitt Gray09db4672021-09-29 12:16:05 +0900237 spin_unlock_irqrestore(&priv->lock, irqflags);
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530238
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900239 return 0;
240}
241
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900242static int quad8_function_write(struct counter_device *counter,
243 struct counter_count *count,
244 enum counter_function function)
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900245{
William Breathitt Graye357e812021-01-30 11:37:03 +0900246 struct quad8 *const priv = counter->priv;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900247 const int id = count->id;
248 unsigned int *const quadrature_mode = priv->quadrature_mode + id;
249 unsigned int *const scale = priv->quadrature_scale + id;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900250 unsigned int *const synchronous_mode = priv->synchronous_mode + id;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900251 const int base_offset = priv->base + 2 * id + 1;
William Breathitt Gray09db4672021-09-29 12:16:05 +0900252 unsigned long irqflags;
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530253 unsigned int mode_cfg;
254 unsigned int idr_cfg;
255
William Breathitt Gray09db4672021-09-29 12:16:05 +0900256 spin_lock_irqsave(&priv->lock, irqflags);
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530257
258 mode_cfg = priv->count_mode[id] << 1;
259 idr_cfg = priv->index_polarity[id] << 1;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900260
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900261 if (function == COUNTER_FUNCTION_PULSE_DIRECTION) {
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900262 *quadrature_mode = 0;
263
264 /* Quadrature scaling only available in quadrature mode */
265 *scale = 0;
266
267 /* Synchronous function not supported in non-quadrature mode */
268 if (*synchronous_mode) {
269 *synchronous_mode = 0;
270 /* Disable synchronous function mode */
271 outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
272 }
273 } else {
274 *quadrature_mode = 1;
275
276 switch (function) {
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900277 case COUNTER_FUNCTION_QUADRATURE_X1_A:
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900278 *scale = 0;
279 mode_cfg |= QUAD8_CMR_QUADRATURE_X1;
280 break;
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900281 case COUNTER_FUNCTION_QUADRATURE_X2_A:
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900282 *scale = 1;
283 mode_cfg |= QUAD8_CMR_QUADRATURE_X2;
284 break;
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900285 case COUNTER_FUNCTION_QUADRATURE_X4:
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900286 *scale = 2;
287 mode_cfg |= QUAD8_CMR_QUADRATURE_X4;
288 break;
William Breathitt Grayb11eed12021-08-03 21:06:12 +0900289 default:
290 /* should never reach this path */
William Breathitt Gray09db4672021-09-29 12:16:05 +0900291 spin_unlock_irqrestore(&priv->lock, irqflags);
William Breathitt Grayb11eed12021-08-03 21:06:12 +0900292 return -EINVAL;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900293 }
294 }
295
296 /* Load mode configuration to Counter Mode Register */
297 outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
298
William Breathitt Gray09db4672021-09-29 12:16:05 +0900299 spin_unlock_irqrestore(&priv->lock, irqflags);
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530300
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900301 return 0;
302}
303
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900304static int quad8_direction_read(struct counter_device *counter,
305 struct counter_count *count,
306 enum counter_count_direction *direction)
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900307{
William Breathitt Graye357e812021-01-30 11:37:03 +0900308 const struct quad8 *const priv = counter->priv;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900309 unsigned int ud_flag;
310 const unsigned int flag_addr = priv->base + 2 * count->id + 1;
311
312 /* U/D flag: nonzero = up, zero = down */
313 ud_flag = inb(flag_addr) & QUAD8_FLAG_UD;
314
315 *direction = (ud_flag) ? COUNTER_COUNT_DIRECTION_FORWARD :
316 COUNTER_COUNT_DIRECTION_BACKWARD;
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900317
318 return 0;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900319}
320
William Breathitt Gray6a9eb0e2021-06-09 10:31:15 +0900321static const enum counter_synapse_action quad8_index_actions_list[] = {
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900322 COUNTER_SYNAPSE_ACTION_NONE,
323 COUNTER_SYNAPSE_ACTION_RISING_EDGE,
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900324};
325
William Breathitt Gray6a9eb0e2021-06-09 10:31:15 +0900326static const enum counter_synapse_action quad8_synapse_actions_list[] = {
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900327 COUNTER_SYNAPSE_ACTION_NONE,
328 COUNTER_SYNAPSE_ACTION_RISING_EDGE,
329 COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
330 COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900331};
332
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900333static int quad8_action_read(struct counter_device *counter,
334 struct counter_count *count,
335 struct counter_synapse *synapse,
336 enum counter_synapse_action *action)
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900337{
William Breathitt Graye357e812021-01-30 11:37:03 +0900338 struct quad8 *const priv = counter->priv;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900339 int err;
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900340 enum counter_function function;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900341 const size_t signal_a_id = count->synapses[0].signal->id;
342 enum counter_count_direction direction;
343
344 /* Handle Index signals */
345 if (synapse->signal->id >= 16) {
346 if (priv->preset_enable[count->id])
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900347 *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900348 else
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900349 *action = COUNTER_SYNAPSE_ACTION_NONE;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900350
351 return 0;
352 }
353
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900354 err = quad8_function_read(counter, count, &function);
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900355 if (err)
356 return err;
357
358 /* Default action mode */
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900359 *action = COUNTER_SYNAPSE_ACTION_NONE;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900360
361 /* Determine action mode based on current count function mode */
362 switch (function) {
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900363 case COUNTER_FUNCTION_PULSE_DIRECTION:
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900364 if (synapse->signal->id == signal_a_id)
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900365 *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
William Breathitt Grayb11eed12021-08-03 21:06:12 +0900366 return 0;
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900367 case COUNTER_FUNCTION_QUADRATURE_X1_A:
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900368 if (synapse->signal->id == signal_a_id) {
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900369 err = quad8_direction_read(counter, count, &direction);
370 if (err)
371 return err;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900372
373 if (direction == COUNTER_COUNT_DIRECTION_FORWARD)
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900374 *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900375 else
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900376 *action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900377 }
William Breathitt Grayb11eed12021-08-03 21:06:12 +0900378 return 0;
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900379 case COUNTER_FUNCTION_QUADRATURE_X2_A:
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900380 if (synapse->signal->id == signal_a_id)
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900381 *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
William Breathitt Grayb11eed12021-08-03 21:06:12 +0900382 return 0;
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900383 case COUNTER_FUNCTION_QUADRATURE_X4:
384 *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
William Breathitt Grayb11eed12021-08-03 21:06:12 +0900385 return 0;
386 default:
387 /* should never reach this path */
388 return -EINVAL;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900389 }
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900390}
391
William Breathitt Gray7aa2ba02021-09-29 12:16:06 +0900392enum {
393 QUAD8_EVENT_NONE = -1,
394 QUAD8_EVENT_CARRY = 0,
395 QUAD8_EVENT_COMPARE = 1,
396 QUAD8_EVENT_CARRY_BORROW = 2,
397 QUAD8_EVENT_INDEX = 3,
398};
399
400static int quad8_events_configure(struct counter_device *counter)
401{
402 struct quad8 *const priv = counter->priv;
403 unsigned long irq_enabled = 0;
404 unsigned long irqflags;
405 size_t channel;
406 unsigned long ior_cfg;
407 unsigned long base_offset;
408
409 spin_lock_irqsave(&priv->lock, irqflags);
410
411 /* Enable interrupts for the requested channels, disable for the rest */
412 for (channel = 0; channel < QUAD8_NUM_COUNTERS; channel++) {
413 if (priv->next_irq_trigger[channel] == QUAD8_EVENT_NONE)
414 continue;
415
416 if (priv->irq_trigger[channel] != priv->next_irq_trigger[channel]) {
417 /* Save new IRQ function configuration */
418 priv->irq_trigger[channel] = priv->next_irq_trigger[channel];
419
420 /* Load configuration to I/O Control Register */
421 ior_cfg = priv->ab_enable[channel] |
422 priv->preset_enable[channel] << 1 |
423 priv->irq_trigger[channel] << 3;
424 base_offset = priv->base + 2 * channel + 1;
425 outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
426 }
427
428 /* Reset next IRQ trigger function configuration */
429 priv->next_irq_trigger[channel] = QUAD8_EVENT_NONE;
430
431 /* Enable IRQ line */
432 irq_enabled |= BIT(channel);
433 }
434
435 outb(irq_enabled, priv->base + QUAD8_REG_INDEX_INTERRUPT);
436
437 spin_unlock_irqrestore(&priv->lock, irqflags);
438
439 return 0;
440}
441
442static int quad8_watch_validate(struct counter_device *counter,
443 const struct counter_watch *watch)
444{
445 struct quad8 *const priv = counter->priv;
446
447 if (watch->channel > QUAD8_NUM_COUNTERS - 1)
448 return -EINVAL;
449
450 switch (watch->event) {
451 case COUNTER_EVENT_OVERFLOW:
452 if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE)
453 priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_CARRY;
454 else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_CARRY)
455 return -EINVAL;
456 return 0;
457 case COUNTER_EVENT_THRESHOLD:
458 if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE)
459 priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_COMPARE;
460 else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_COMPARE)
461 return -EINVAL;
462 return 0;
463 case COUNTER_EVENT_OVERFLOW_UNDERFLOW:
464 if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE)
465 priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_CARRY_BORROW;
466 else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_CARRY_BORROW)
467 return -EINVAL;
468 return 0;
469 case COUNTER_EVENT_INDEX:
470 if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE)
471 priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_INDEX;
472 else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_INDEX)
473 return -EINVAL;
474 return 0;
475 default:
476 return -EINVAL;
477 }
478}
479
YueHaibing17aa2072019-04-27 00:48:16 +0800480static const struct counter_ops quad8_ops = {
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900481 .signal_read = quad8_signal_read,
482 .count_read = quad8_count_read,
483 .count_write = quad8_count_write,
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900484 .function_read = quad8_function_read,
485 .function_write = quad8_function_write,
William Breathitt Gray7aa2ba02021-09-29 12:16:06 +0900486 .action_read = quad8_action_read,
487 .events_configure = quad8_events_configure,
488 .watch_validate = quad8_watch_validate,
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900489};
490
William Breathitt Graye357e812021-01-30 11:37:03 +0900491static const char *const quad8_index_polarity_modes[] = {
492 "negative",
493 "positive"
494};
495
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900496static int quad8_index_polarity_get(struct counter_device *counter,
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900497 struct counter_signal *signal,
498 u32 *index_polarity)
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900499{
William Breathitt Graye357e812021-01-30 11:37:03 +0900500 const struct quad8 *const priv = counter->priv;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900501 const size_t channel_id = signal->id - 16;
502
503 *index_polarity = priv->index_polarity[channel_id];
504
505 return 0;
506}
507
508static int quad8_index_polarity_set(struct counter_device *counter,
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900509 struct counter_signal *signal,
510 u32 index_polarity)
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900511{
William Breathitt Graye357e812021-01-30 11:37:03 +0900512 struct quad8 *const priv = counter->priv;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900513 const size_t channel_id = signal->id - 16;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900514 const int base_offset = priv->base + 2 * channel_id + 1;
William Breathitt Gray09db4672021-09-29 12:16:05 +0900515 unsigned long irqflags;
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530516 unsigned int idr_cfg = index_polarity << 1;
517
William Breathitt Gray09db4672021-09-29 12:16:05 +0900518 spin_lock_irqsave(&priv->lock, irqflags);
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530519
520 idr_cfg |= priv->synchronous_mode[channel_id];
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900521
522 priv->index_polarity[channel_id] = index_polarity;
523
524 /* Load Index Control configuration to Index Control Register */
525 outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
526
William Breathitt Gray09db4672021-09-29 12:16:05 +0900527 spin_unlock_irqrestore(&priv->lock, irqflags);
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530528
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900529 return 0;
530}
531
William Breathitt Graye357e812021-01-30 11:37:03 +0900532static const char *const quad8_synchronous_modes[] = {
533 "non-synchronous",
534 "synchronous"
535};
536
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900537static int quad8_synchronous_mode_get(struct counter_device *counter,
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900538 struct counter_signal *signal,
539 u32 *synchronous_mode)
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900540{
William Breathitt Graye357e812021-01-30 11:37:03 +0900541 const struct quad8 *const priv = counter->priv;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900542 const size_t channel_id = signal->id - 16;
543
544 *synchronous_mode = priv->synchronous_mode[channel_id];
545
546 return 0;
547}
548
549static int quad8_synchronous_mode_set(struct counter_device *counter,
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900550 struct counter_signal *signal,
551 u32 synchronous_mode)
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900552{
William Breathitt Graye357e812021-01-30 11:37:03 +0900553 struct quad8 *const priv = counter->priv;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900554 const size_t channel_id = signal->id - 16;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900555 const int base_offset = priv->base + 2 * channel_id + 1;
William Breathitt Gray09db4672021-09-29 12:16:05 +0900556 unsigned long irqflags;
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530557 unsigned int idr_cfg = synchronous_mode;
558
William Breathitt Gray09db4672021-09-29 12:16:05 +0900559 spin_lock_irqsave(&priv->lock, irqflags);
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530560
561 idr_cfg |= priv->index_polarity[channel_id] << 1;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900562
563 /* Index function must be non-synchronous in non-quadrature mode */
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530564 if (synchronous_mode && !priv->quadrature_mode[channel_id]) {
William Breathitt Gray09db4672021-09-29 12:16:05 +0900565 spin_unlock_irqrestore(&priv->lock, irqflags);
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900566 return -EINVAL;
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530567 }
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900568
569 priv->synchronous_mode[channel_id] = synchronous_mode;
570
571 /* Load Index Control configuration to Index Control Register */
572 outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
573
William Breathitt Gray09db4672021-09-29 12:16:05 +0900574 spin_unlock_irqrestore(&priv->lock, irqflags);
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530575
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900576 return 0;
577}
578
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900579static int quad8_count_floor_read(struct counter_device *counter,
580 struct counter_count *count, u64 *floor)
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900581{
582 /* Only a floor of 0 is supported */
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900583 *floor = 0;
584
585 return 0;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900586}
587
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900588static int quad8_count_mode_read(struct counter_device *counter,
589 struct counter_count *count,
590 enum counter_count_mode *cnt_mode)
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900591{
William Breathitt Graye357e812021-01-30 11:37:03 +0900592 const struct quad8 *const priv = counter->priv;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900593
594 /* Map 104-QUAD-8 count mode to Generic Counter count mode */
595 switch (priv->count_mode[count->id]) {
596 case 0:
597 *cnt_mode = COUNTER_COUNT_MODE_NORMAL;
598 break;
599 case 1:
600 *cnt_mode = COUNTER_COUNT_MODE_RANGE_LIMIT;
601 break;
602 case 2:
603 *cnt_mode = COUNTER_COUNT_MODE_NON_RECYCLE;
604 break;
605 case 3:
606 *cnt_mode = COUNTER_COUNT_MODE_MODULO_N;
607 break;
608 }
609
610 return 0;
611}
612
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900613static int quad8_count_mode_write(struct counter_device *counter,
614 struct counter_count *count,
615 enum counter_count_mode cnt_mode)
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900616{
William Breathitt Graye357e812021-01-30 11:37:03 +0900617 struct quad8 *const priv = counter->priv;
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900618 unsigned int count_mode;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900619 unsigned int mode_cfg;
620 const int base_offset = priv->base + 2 * count->id + 1;
William Breathitt Gray09db4672021-09-29 12:16:05 +0900621 unsigned long irqflags;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900622
623 /* Map Generic Counter count mode to 104-QUAD-8 count mode */
624 switch (cnt_mode) {
625 case COUNTER_COUNT_MODE_NORMAL:
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900626 count_mode = 0;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900627 break;
628 case COUNTER_COUNT_MODE_RANGE_LIMIT:
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900629 count_mode = 1;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900630 break;
631 case COUNTER_COUNT_MODE_NON_RECYCLE:
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900632 count_mode = 2;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900633 break;
634 case COUNTER_COUNT_MODE_MODULO_N:
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900635 count_mode = 3;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900636 break;
William Breathitt Grayb11eed12021-08-03 21:06:12 +0900637 default:
638 /* should never reach this path */
639 return -EINVAL;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900640 }
641
William Breathitt Gray09db4672021-09-29 12:16:05 +0900642 spin_lock_irqsave(&priv->lock, irqflags);
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530643
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900644 priv->count_mode[count->id] = count_mode;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900645
646 /* Set count mode configuration value */
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900647 mode_cfg = count_mode << 1;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900648
649 /* Add quadrature mode configuration */
650 if (priv->quadrature_mode[count->id])
651 mode_cfg |= (priv->quadrature_scale[count->id] + 1) << 3;
652
653 /* Load mode configuration to Counter Mode Register */
654 outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
655
William Breathitt Gray09db4672021-09-29 12:16:05 +0900656 spin_unlock_irqrestore(&priv->lock, irqflags);
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530657
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900658 return 0;
659}
660
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900661static int quad8_count_enable_read(struct counter_device *counter,
662 struct counter_count *count, u8 *enable)
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900663{
William Breathitt Graye357e812021-01-30 11:37:03 +0900664 const struct quad8 *const priv = counter->priv;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900665
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900666 *enable = priv->ab_enable[count->id];
667
668 return 0;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900669}
670
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900671static int quad8_count_enable_write(struct counter_device *counter,
672 struct counter_count *count, u8 enable)
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900673{
William Breathitt Graye357e812021-01-30 11:37:03 +0900674 struct quad8 *const priv = counter->priv;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900675 const int base_offset = priv->base + 2 * count->id;
William Breathitt Gray09db4672021-09-29 12:16:05 +0900676 unsigned long irqflags;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900677 unsigned int ior_cfg;
678
William Breathitt Gray09db4672021-09-29 12:16:05 +0900679 spin_lock_irqsave(&priv->lock, irqflags);
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530680
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900681 priv->ab_enable[count->id] = enable;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900682
William Breathitt Gray7aa2ba02021-09-29 12:16:06 +0900683 ior_cfg = enable | priv->preset_enable[count->id] << 1 |
684 priv->irq_trigger[count->id] << 3;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900685
686 /* Load I/O control configuration */
687 outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
688
William Breathitt Gray09db4672021-09-29 12:16:05 +0900689 spin_unlock_irqrestore(&priv->lock, irqflags);
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530690
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900691 return 0;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900692}
693
William Breathitt Graye357e812021-01-30 11:37:03 +0900694static const char *const quad8_noise_error_states[] = {
695 "No excessive noise is present at the count inputs",
696 "Excessive noise is present at the count inputs"
697};
698
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900699static int quad8_error_noise_get(struct counter_device *counter,
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900700 struct counter_count *count, u32 *noise_error)
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900701{
William Breathitt Graye357e812021-01-30 11:37:03 +0900702 const struct quad8 *const priv = counter->priv;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900703 const int base_offset = priv->base + 2 * count->id + 1;
704
705 *noise_error = !!(inb(base_offset) & QUAD8_FLAG_E);
706
707 return 0;
708}
709
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900710static int quad8_count_preset_read(struct counter_device *counter,
711 struct counter_count *count, u64 *preset)
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900712{
William Breathitt Graye357e812021-01-30 11:37:03 +0900713 const struct quad8 *const priv = counter->priv;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900714
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900715 *preset = priv->preset[count->id];
716
717 return 0;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900718}
719
William Breathitt Graye612b602021-06-09 10:31:09 +0900720static void quad8_preset_register_set(struct quad8 *const priv, const int id,
721 const unsigned int preset)
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530722{
William Breathitt Graye357e812021-01-30 11:37:03 +0900723 const unsigned int base_offset = priv->base + 2 * id;
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530724 int i;
725
William Breathitt Graye357e812021-01-30 11:37:03 +0900726 priv->preset[id] = preset;
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530727
728 /* Reset Byte Pointer */
729 outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
730
731 /* Set Preset Register */
732 for (i = 0; i < 3; i++)
733 outb(preset >> (8 * i), base_offset);
734}
735
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900736static int quad8_count_preset_write(struct counter_device *counter,
737 struct counter_count *count, u64 preset)
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900738{
William Breathitt Graye357e812021-01-30 11:37:03 +0900739 struct quad8 *const priv = counter->priv;
William Breathitt Gray09db4672021-09-29 12:16:05 +0900740 unsigned long irqflags;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900741
742 /* Only 24-bit values are supported */
743 if (preset > 0xFFFFFF)
William Breathitt Graye2ff3192021-08-03 21:06:13 +0900744 return -ERANGE;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900745
William Breathitt Gray09db4672021-09-29 12:16:05 +0900746 spin_lock_irqsave(&priv->lock, irqflags);
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900747
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530748 quad8_preset_register_set(priv, count->id, preset);
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900749
William Breathitt Gray09db4672021-09-29 12:16:05 +0900750 spin_unlock_irqrestore(&priv->lock, irqflags);
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900751
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900752 return 0;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900753}
754
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900755static int quad8_count_ceiling_read(struct counter_device *counter,
756 struct counter_count *count, u64 *ceiling)
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900757{
William Breathitt Graye357e812021-01-30 11:37:03 +0900758 struct quad8 *const priv = counter->priv;
William Breathitt Gray09db4672021-09-29 12:16:05 +0900759 unsigned long irqflags;
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530760
William Breathitt Gray09db4672021-09-29 12:16:05 +0900761 spin_lock_irqsave(&priv->lock, irqflags);
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900762
763 /* Range Limit and Modulo-N count modes use preset value as ceiling */
764 switch (priv->count_mode[count->id]) {
765 case 1:
766 case 3:
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900767 *ceiling = priv->preset[count->id];
768 break;
769 default:
770 /* By default 0x1FFFFFF (25 bits unsigned) is maximum count */
771 *ceiling = 0x1FFFFFF;
772 break;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900773 }
774
William Breathitt Gray09db4672021-09-29 12:16:05 +0900775 spin_unlock_irqrestore(&priv->lock, irqflags);
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530776
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900777 return 0;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900778}
779
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900780static int quad8_count_ceiling_write(struct counter_device *counter,
781 struct counter_count *count, u64 ceiling)
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900782{
William Breathitt Graye357e812021-01-30 11:37:03 +0900783 struct quad8 *const priv = counter->priv;
William Breathitt Gray09db4672021-09-29 12:16:05 +0900784 unsigned long irqflags;
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530785
786 /* Only 24-bit values are supported */
787 if (ceiling > 0xFFFFFF)
William Breathitt Graye2ff3192021-08-03 21:06:13 +0900788 return -ERANGE;
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530789
William Breathitt Gray09db4672021-09-29 12:16:05 +0900790 spin_lock_irqsave(&priv->lock, irqflags);
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900791
792 /* Range Limit and Modulo-N count modes use preset value as ceiling */
793 switch (priv->count_mode[count->id]) {
794 case 1:
795 case 3:
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530796 quad8_preset_register_set(priv, count->id, ceiling);
William Breathitt Gray09db4672021-09-29 12:16:05 +0900797 spin_unlock_irqrestore(&priv->lock, irqflags);
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900798 return 0;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900799 }
800
William Breathitt Gray09db4672021-09-29 12:16:05 +0900801 spin_unlock_irqrestore(&priv->lock, irqflags);
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530802
William Breathitt Gray728246e2021-08-03 21:06:11 +0900803 return -EINVAL;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900804}
805
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900806static int quad8_count_preset_enable_read(struct counter_device *counter,
807 struct counter_count *count,
808 u8 *preset_enable)
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900809{
William Breathitt Graye357e812021-01-30 11:37:03 +0900810 const struct quad8 *const priv = counter->priv;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900811
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900812 *preset_enable = !priv->preset_enable[count->id];
813
814 return 0;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900815}
816
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900817static int quad8_count_preset_enable_write(struct counter_device *counter,
818 struct counter_count *count,
819 u8 preset_enable)
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900820{
William Breathitt Graye357e812021-01-30 11:37:03 +0900821 struct quad8 *const priv = counter->priv;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900822 const int base_offset = priv->base + 2 * count->id + 1;
William Breathitt Gray09db4672021-09-29 12:16:05 +0900823 unsigned long irqflags;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900824 unsigned int ior_cfg;
825
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900826 /* Preset enable is active low in Input/Output Control register */
827 preset_enable = !preset_enable;
828
William Breathitt Gray09db4672021-09-29 12:16:05 +0900829 spin_lock_irqsave(&priv->lock, irqflags);
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530830
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900831 priv->preset_enable[count->id] = preset_enable;
832
William Breathitt Gray7aa2ba02021-09-29 12:16:06 +0900833 ior_cfg = priv->ab_enable[count->id] | preset_enable << 1 |
834 priv->irq_trigger[count->id] << 3;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900835
836 /* Load I/O control configuration to Input / Output Control Register */
837 outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
838
William Breathitt Gray09db4672021-09-29 12:16:05 +0900839 spin_unlock_irqrestore(&priv->lock, irqflags);
Syed Nayyar Warisfc069262020-03-16 18:19:30 +0530840
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900841 return 0;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900842}
843
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900844static int quad8_signal_cable_fault_read(struct counter_device *counter,
845 struct counter_signal *signal,
846 u8 *cable_fault)
William Breathitt Gray954ab5c2020-03-01 17:07:19 -0500847{
William Breathitt Graye357e812021-01-30 11:37:03 +0900848 struct quad8 *const priv = counter->priv;
William Breathitt Gray954ab5c2020-03-01 17:07:19 -0500849 const size_t channel_id = signal->id / 2;
William Breathitt Gray09db4672021-09-29 12:16:05 +0900850 unsigned long irqflags;
Syed Nayyar Waris708d98932020-03-16 18:20:06 +0530851 bool disabled;
William Breathitt Gray954ab5c2020-03-01 17:07:19 -0500852 unsigned int status;
William Breathitt Gray954ab5c2020-03-01 17:07:19 -0500853
William Breathitt Gray09db4672021-09-29 12:16:05 +0900854 spin_lock_irqsave(&priv->lock, irqflags);
Syed Nayyar Waris708d98932020-03-16 18:20:06 +0530855
856 disabled = !(priv->cable_fault_enable & BIT(channel_id));
857
858 if (disabled) {
William Breathitt Gray09db4672021-09-29 12:16:05 +0900859 spin_unlock_irqrestore(&priv->lock, irqflags);
William Breathitt Gray954ab5c2020-03-01 17:07:19 -0500860 return -EINVAL;
Syed Nayyar Waris708d98932020-03-16 18:20:06 +0530861 }
William Breathitt Gray954ab5c2020-03-01 17:07:19 -0500862
863 /* Logic 0 = cable fault */
864 status = inb(priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
865
William Breathitt Gray09db4672021-09-29 12:16:05 +0900866 spin_unlock_irqrestore(&priv->lock, irqflags);
Syed Nayyar Waris708d98932020-03-16 18:20:06 +0530867
William Breathitt Gray954ab5c2020-03-01 17:07:19 -0500868 /* Mask respective channel and invert logic */
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900869 *cable_fault = !(status & BIT(channel_id));
William Breathitt Gray954ab5c2020-03-01 17:07:19 -0500870
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900871 return 0;
William Breathitt Gray954ab5c2020-03-01 17:07:19 -0500872}
873
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900874static int quad8_signal_cable_fault_enable_read(struct counter_device *counter,
875 struct counter_signal *signal,
876 u8 *enable)
William Breathitt Gray954ab5c2020-03-01 17:07:19 -0500877{
William Breathitt Graye357e812021-01-30 11:37:03 +0900878 const struct quad8 *const priv = counter->priv;
William Breathitt Gray954ab5c2020-03-01 17:07:19 -0500879 const size_t channel_id = signal->id / 2;
William Breathitt Gray954ab5c2020-03-01 17:07:19 -0500880
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900881 *enable = !!(priv->cable_fault_enable & BIT(channel_id));
882
883 return 0;
William Breathitt Gray954ab5c2020-03-01 17:07:19 -0500884}
885
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900886static int quad8_signal_cable_fault_enable_write(struct counter_device *counter,
887 struct counter_signal *signal,
888 u8 enable)
William Breathitt Gray954ab5c2020-03-01 17:07:19 -0500889{
William Breathitt Graye357e812021-01-30 11:37:03 +0900890 struct quad8 *const priv = counter->priv;
William Breathitt Gray954ab5c2020-03-01 17:07:19 -0500891 const size_t channel_id = signal->id / 2;
William Breathitt Gray09db4672021-09-29 12:16:05 +0900892 unsigned long irqflags;
William Breathitt Gray954ab5c2020-03-01 17:07:19 -0500893 unsigned int cable_fault_enable;
894
William Breathitt Gray09db4672021-09-29 12:16:05 +0900895 spin_lock_irqsave(&priv->lock, irqflags);
Syed Nayyar Waris708d98932020-03-16 18:20:06 +0530896
William Breathitt Gray954ab5c2020-03-01 17:07:19 -0500897 if (enable)
898 priv->cable_fault_enable |= BIT(channel_id);
899 else
900 priv->cable_fault_enable &= ~BIT(channel_id);
901
902 /* Enable is active low in Differential Encoder Cable Status register */
903 cable_fault_enable = ~priv->cable_fault_enable;
904
905 outb(cable_fault_enable, priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
906
William Breathitt Gray09db4672021-09-29 12:16:05 +0900907 spin_unlock_irqrestore(&priv->lock, irqflags);
Syed Nayyar Waris708d98932020-03-16 18:20:06 +0530908
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900909 return 0;
William Breathitt Gray954ab5c2020-03-01 17:07:19 -0500910}
911
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900912static int quad8_signal_fck_prescaler_read(struct counter_device *counter,
913 struct counter_signal *signal,
914 u8 *prescaler)
William Breathitt Grayde65d052020-02-22 11:49:58 -0500915{
William Breathitt Graye357e812021-01-30 11:37:03 +0900916 const struct quad8 *const priv = counter->priv;
William Breathitt Grayde65d052020-02-22 11:49:58 -0500917
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900918 *prescaler = priv->fck_prescaler[signal->id / 2];
919
920 return 0;
William Breathitt Grayde65d052020-02-22 11:49:58 -0500921}
922
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900923static int quad8_signal_fck_prescaler_write(struct counter_device *counter,
924 struct counter_signal *signal,
925 u8 prescaler)
William Breathitt Grayde65d052020-02-22 11:49:58 -0500926{
William Breathitt Graye357e812021-01-30 11:37:03 +0900927 struct quad8 *const priv = counter->priv;
William Breathitt Grayde65d052020-02-22 11:49:58 -0500928 const size_t channel_id = signal->id / 2;
929 const int base_offset = priv->base + 2 * channel_id;
William Breathitt Gray09db4672021-09-29 12:16:05 +0900930 unsigned long irqflags;
William Breathitt Grayde65d052020-02-22 11:49:58 -0500931
William Breathitt Gray09db4672021-09-29 12:16:05 +0900932 spin_lock_irqsave(&priv->lock, irqflags);
Syed Nayyar Warisd5ed76a2020-03-16 18:20:46 +0530933
William Breathitt Grayde65d052020-02-22 11:49:58 -0500934 priv->fck_prescaler[channel_id] = prescaler;
935
936 /* Reset Byte Pointer */
937 outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
938
939 /* Set filter clock factor */
940 outb(prescaler, base_offset);
941 outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
942 base_offset + 1);
943
William Breathitt Gray09db4672021-09-29 12:16:05 +0900944 spin_unlock_irqrestore(&priv->lock, irqflags);
Syed Nayyar Warisd5ed76a2020-03-16 18:20:46 +0530945
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900946 return 0;
William Breathitt Grayde65d052020-02-22 11:49:58 -0500947}
948
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900949static struct counter_comp quad8_signal_ext[] = {
950 COUNTER_COMP_SIGNAL_BOOL("cable_fault", quad8_signal_cable_fault_read,
951 NULL),
952 COUNTER_COMP_SIGNAL_BOOL("cable_fault_enable",
953 quad8_signal_cable_fault_enable_read,
954 quad8_signal_cable_fault_enable_write),
955 COUNTER_COMP_SIGNAL_U8("filter_clock_prescaler",
956 quad8_signal_fck_prescaler_read,
957 quad8_signal_fck_prescaler_write)
William Breathitt Grayde65d052020-02-22 11:49:58 -0500958};
959
William Breathitt Grayaaec1a02021-08-27 12:47:47 +0900960static DEFINE_COUNTER_ENUM(quad8_index_pol_enum, quad8_index_polarity_modes);
961static DEFINE_COUNTER_ENUM(quad8_synch_mode_enum, quad8_synchronous_modes);
962
963static struct counter_comp quad8_index_ext[] = {
964 COUNTER_COMP_SIGNAL_ENUM("index_polarity", quad8_index_polarity_get,
965 quad8_index_polarity_set,
966 quad8_index_pol_enum),
967 COUNTER_COMP_SIGNAL_ENUM("synchronous_mode", quad8_synchronous_mode_get,
968 quad8_synchronous_mode_set,
969 quad8_synch_mode_enum),
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900970};
971
William Breathitt Grayde65d052020-02-22 11:49:58 -0500972#define QUAD8_QUAD_SIGNAL(_id, _name) { \
973 .id = (_id), \
974 .name = (_name), \
975 .ext = quad8_signal_ext, \
976 .num_ext = ARRAY_SIZE(quad8_signal_ext) \
William Breathitt Grayf1d8a072019-04-02 15:30:40 +0900977}
978
979#define QUAD8_INDEX_SIGNAL(_id, _name) { \
980 .id = (_id), \
981 .name = (_name), \
982 .ext = quad8_index_ext, \
983 .num_ext = ARRAY_SIZE(quad8_index_ext) \
984}
985
986static struct counter_signal quad8_signals[] = {
987 QUAD8_QUAD_SIGNAL(0, "Channel 1 Quadrature A"),
988 QUAD8_QUAD_SIGNAL(1, "Channel 1 Quadrature B"),
989 QUAD8_QUAD_SIGNAL(2, "Channel 2 Quadrature A"),
990 QUAD8_QUAD_SIGNAL(3, "Channel 2 Quadrature B"),
991 QUAD8_QUAD_SIGNAL(4, "Channel 3 Quadrature A"),
992 QUAD8_QUAD_SIGNAL(5, "Channel 3 Quadrature B"),
993 QUAD8_QUAD_SIGNAL(6, "Channel 4 Quadrature A"),
994 QUAD8_QUAD_SIGNAL(7, "Channel 4 Quadrature B"),
995 QUAD8_QUAD_SIGNAL(8, "Channel 5 Quadrature A"),
996 QUAD8_QUAD_SIGNAL(9, "Channel 5 Quadrature B"),
997 QUAD8_QUAD_SIGNAL(10, "Channel 6 Quadrature A"),
998 QUAD8_QUAD_SIGNAL(11, "Channel 6 Quadrature B"),
999 QUAD8_QUAD_SIGNAL(12, "Channel 7 Quadrature A"),
1000 QUAD8_QUAD_SIGNAL(13, "Channel 7 Quadrature B"),
1001 QUAD8_QUAD_SIGNAL(14, "Channel 8 Quadrature A"),
1002 QUAD8_QUAD_SIGNAL(15, "Channel 8 Quadrature B"),
1003 QUAD8_INDEX_SIGNAL(16, "Channel 1 Index"),
1004 QUAD8_INDEX_SIGNAL(17, "Channel 2 Index"),
1005 QUAD8_INDEX_SIGNAL(18, "Channel 3 Index"),
1006 QUAD8_INDEX_SIGNAL(19, "Channel 4 Index"),
1007 QUAD8_INDEX_SIGNAL(20, "Channel 5 Index"),
1008 QUAD8_INDEX_SIGNAL(21, "Channel 6 Index"),
1009 QUAD8_INDEX_SIGNAL(22, "Channel 7 Index"),
1010 QUAD8_INDEX_SIGNAL(23, "Channel 8 Index")
1011};
1012
1013#define QUAD8_COUNT_SYNAPSES(_id) { \
1014 { \
1015 .actions_list = quad8_synapse_actions_list, \
1016 .num_actions = ARRAY_SIZE(quad8_synapse_actions_list), \
1017 .signal = quad8_signals + 2 * (_id) \
1018 }, \
1019 { \
1020 .actions_list = quad8_synapse_actions_list, \
1021 .num_actions = ARRAY_SIZE(quad8_synapse_actions_list), \
1022 .signal = quad8_signals + 2 * (_id) + 1 \
1023 }, \
1024 { \
1025 .actions_list = quad8_index_actions_list, \
1026 .num_actions = ARRAY_SIZE(quad8_index_actions_list), \
1027 .signal = quad8_signals + 2 * (_id) + 16 \
1028 } \
1029}
1030
1031static struct counter_synapse quad8_count_synapses[][3] = {
1032 QUAD8_COUNT_SYNAPSES(0), QUAD8_COUNT_SYNAPSES(1),
1033 QUAD8_COUNT_SYNAPSES(2), QUAD8_COUNT_SYNAPSES(3),
1034 QUAD8_COUNT_SYNAPSES(4), QUAD8_COUNT_SYNAPSES(5),
1035 QUAD8_COUNT_SYNAPSES(6), QUAD8_COUNT_SYNAPSES(7)
1036};
1037
William Breathitt Grayaaec1a02021-08-27 12:47:47 +09001038static const enum counter_count_mode quad8_cnt_modes[] = {
1039 COUNTER_COUNT_MODE_NORMAL,
1040 COUNTER_COUNT_MODE_RANGE_LIMIT,
1041 COUNTER_COUNT_MODE_NON_RECYCLE,
1042 COUNTER_COUNT_MODE_MODULO_N,
1043};
1044
1045static DEFINE_COUNTER_AVAILABLE(quad8_count_mode_available, quad8_cnt_modes);
1046
1047static DEFINE_COUNTER_ENUM(quad8_error_noise_enum, quad8_noise_error_states);
1048
1049static struct counter_comp quad8_count_ext[] = {
1050 COUNTER_COMP_CEILING(quad8_count_ceiling_read,
1051 quad8_count_ceiling_write),
1052 COUNTER_COMP_FLOOR(quad8_count_floor_read, NULL),
1053 COUNTER_COMP_COUNT_MODE(quad8_count_mode_read, quad8_count_mode_write,
1054 quad8_count_mode_available),
1055 COUNTER_COMP_DIRECTION(quad8_direction_read),
1056 COUNTER_COMP_ENABLE(quad8_count_enable_read, quad8_count_enable_write),
1057 COUNTER_COMP_COUNT_ENUM("error_noise", quad8_error_noise_get, NULL,
1058 quad8_error_noise_enum),
1059 COUNTER_COMP_PRESET(quad8_count_preset_read, quad8_count_preset_write),
1060 COUNTER_COMP_PRESET_ENABLE(quad8_count_preset_enable_read,
1061 quad8_count_preset_enable_write),
William Breathitt Grayf1d8a072019-04-02 15:30:40 +09001062};
1063
1064#define QUAD8_COUNT(_id, _cntname) { \
1065 .id = (_id), \
1066 .name = (_cntname), \
1067 .functions_list = quad8_count_functions_list, \
1068 .num_functions = ARRAY_SIZE(quad8_count_functions_list), \
1069 .synapses = quad8_count_synapses[(_id)], \
1070 .num_synapses = 2, \
1071 .ext = quad8_count_ext, \
1072 .num_ext = ARRAY_SIZE(quad8_count_ext) \
1073}
1074
1075static struct counter_count quad8_counts[] = {
1076 QUAD8_COUNT(0, "Channel 1 Count"),
1077 QUAD8_COUNT(1, "Channel 2 Count"),
1078 QUAD8_COUNT(2, "Channel 3 Count"),
1079 QUAD8_COUNT(3, "Channel 4 Count"),
1080 QUAD8_COUNT(4, "Channel 5 Count"),
1081 QUAD8_COUNT(5, "Channel 6 Count"),
1082 QUAD8_COUNT(6, "Channel 7 Count"),
1083 QUAD8_COUNT(7, "Channel 8 Count")
1084};
1085
William Breathitt Gray7aa2ba02021-09-29 12:16:06 +09001086static irqreturn_t quad8_irq_handler(int irq, void *private)
1087{
1088 struct quad8 *const priv = private;
1089 const unsigned long base = priv->base;
1090 unsigned long irq_status;
1091 unsigned long channel;
1092 u8 event;
1093
1094 irq_status = inb(base + QUAD8_REG_INTERRUPT_STATUS);
1095 if (!irq_status)
1096 return IRQ_NONE;
1097
1098 for_each_set_bit(channel, &irq_status, QUAD8_NUM_COUNTERS) {
1099 switch (priv->irq_trigger[channel]) {
1100 case QUAD8_EVENT_CARRY:
1101 event = COUNTER_EVENT_OVERFLOW;
1102 break;
1103 case QUAD8_EVENT_COMPARE:
1104 event = COUNTER_EVENT_THRESHOLD;
1105 break;
1106 case QUAD8_EVENT_CARRY_BORROW:
1107 event = COUNTER_EVENT_OVERFLOW_UNDERFLOW;
1108 break;
1109 case QUAD8_EVENT_INDEX:
1110 event = COUNTER_EVENT_INDEX;
1111 break;
1112 default:
1113 /* should never reach this path */
1114 WARN_ONCE(true, "invalid interrupt trigger function %u configured for channel %lu\n",
1115 priv->irq_trigger[channel], channel);
1116 continue;
1117 }
1118
1119 counter_push_event(&priv->counter, event, channel);
1120 }
1121
1122 /* Clear pending interrupts on device */
1123 outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base + QUAD8_REG_CHAN_OP);
1124
1125 return IRQ_HANDLED;
1126}
1127
William Breathitt Grayf1d8a072019-04-02 15:30:40 +09001128static int quad8_probe(struct device *dev, unsigned int id)
1129{
William Breathitt Graye357e812021-01-30 11:37:03 +09001130 struct quad8 *priv;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +09001131 int i, j;
1132 unsigned int base_offset;
William Breathitt Gray7aa2ba02021-09-29 12:16:06 +09001133 int err;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +09001134
1135 if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) {
1136 dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
1137 base[id], base[id] + QUAD8_EXTENT);
1138 return -EBUSY;
1139 }
1140
William Breathitt Graye357e812021-01-30 11:37:03 +09001141 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
1142 if (!priv)
William Breathitt Grayf1d8a072019-04-02 15:30:40 +09001143 return -ENOMEM;
1144
William Breathitt Grayf1d8a072019-04-02 15:30:40 +09001145 /* Initialize Counter device and driver data */
William Breathitt Graye357e812021-01-30 11:37:03 +09001146 priv->counter.name = dev_name(dev);
1147 priv->counter.parent = dev;
1148 priv->counter.ops = &quad8_ops;
1149 priv->counter.counts = quad8_counts;
1150 priv->counter.num_counts = ARRAY_SIZE(quad8_counts);
1151 priv->counter.signals = quad8_signals;
1152 priv->counter.num_signals = ARRAY_SIZE(quad8_signals);
1153 priv->counter.priv = priv;
1154 priv->base = base[id];
William Breathitt Grayf1d8a072019-04-02 15:30:40 +09001155
William Breathitt Gray09db4672021-09-29 12:16:05 +09001156 spin_lock_init(&priv->lock);
Syed Nayyar Warisfc069262020-03-16 18:19:30 +05301157
William Breathitt Gray7aa2ba02021-09-29 12:16:06 +09001158 /* Reset Index/Interrupt Register */
1159 outb(0x00, base[id] + QUAD8_REG_INDEX_INTERRUPT);
William Breathitt Grayf1d8a072019-04-02 15:30:40 +09001160 /* Reset all counters and disable interrupt function */
1161 outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
1162 /* Set initial configuration for all counters */
1163 for (i = 0; i < QUAD8_NUM_COUNTERS; i++) {
1164 base_offset = base[id] + 2 * i;
1165 /* Reset Byte Pointer */
1166 outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
William Breathitt Grayde65d052020-02-22 11:49:58 -05001167 /* Reset filter clock factor */
1168 outb(0, base_offset);
1169 outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
1170 base_offset + 1);
1171 /* Reset Byte Pointer */
1172 outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
William Breathitt Grayf1d8a072019-04-02 15:30:40 +09001173 /* Reset Preset Register */
1174 for (j = 0; j < 3; j++)
1175 outb(0x00, base_offset);
1176 /* Reset Borrow, Carry, Compare, and Sign flags */
1177 outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
1178 /* Reset Error flag */
1179 outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
1180 /* Binary encoding; Normal count; non-quadrature mode */
1181 outb(QUAD8_CTR_CMR, base_offset + 1);
1182 /* Disable A and B inputs; preset on index; FLG1 as Carry */
1183 outb(QUAD8_CTR_IOR, base_offset + 1);
1184 /* Disable index function; negative index polarity */
1185 outb(QUAD8_CTR_IDR, base_offset + 1);
William Breathitt Gray7aa2ba02021-09-29 12:16:06 +09001186 /* Initialize next IRQ trigger function configuration */
1187 priv->next_irq_trigger[i] = QUAD8_EVENT_NONE;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +09001188 }
William Breathitt Gray954ab5c2020-03-01 17:07:19 -05001189 /* Disable Differential Encoder Cable Status for all channels */
1190 outb(0xFF, base[id] + QUAD8_DIFF_ENCODER_CABLE_STATUS);
William Breathitt Gray7aa2ba02021-09-29 12:16:06 +09001191 /* Enable all counters and enable interrupt function */
1192 outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base[id] + QUAD8_REG_CHAN_OP);
1193
1194 err = devm_request_irq(dev, irq[id], quad8_irq_handler, IRQF_SHARED,
1195 priv->counter.name, priv);
1196 if (err)
1197 return err;
William Breathitt Grayf1d8a072019-04-02 15:30:40 +09001198
William Breathitt Graye357e812021-01-30 11:37:03 +09001199 return devm_counter_register(dev, &priv->counter);
William Breathitt Grayf1d8a072019-04-02 15:30:40 +09001200}
1201
1202static struct isa_driver quad8_driver = {
1203 .probe = quad8_probe,
1204 .driver = {
1205 .name = "104-quad-8"
1206 }
1207};
1208
1209module_isa_driver(quad8_driver, num_quad8);
1210
1211MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
William Breathitt Graye357e812021-01-30 11:37:03 +09001212MODULE_DESCRIPTION("ACCES 104-QUAD-8 driver");
William Breathitt Grayf1d8a072019-04-02 15:30:40 +09001213MODULE_LICENSE("GPL v2");