1 // SPDX-License-Identifier: GPL-2.0
3 * Counter driver for the ACCES 104-QUAD-8
4 * Copyright (C) 2016 William Breathitt Gray
6 * This driver supports the ACCES 104-QUAD-8 and ACCES 104-QUAD-4.
8 #include <linux/bitops.h>
9 #include <linux/counter.h>
10 #include <linux/device.h>
11 #include <linux/errno.h>
13 #include <linux/ioport.h>
14 #include <linux/interrupt.h>
15 #include <linux/isa.h>
16 #include <linux/kernel.h>
17 #include <linux/module.h>
18 #include <linux/moduleparam.h>
19 #include <linux/types.h>
20 #include <linux/spinlock.h>
22 #define QUAD8_EXTENT 32
24 static unsigned int base[max_num_isa_dev(QUAD8_EXTENT)];
25 static unsigned int num_quad8;
26 module_param_hw_array(base, uint, ioport, &num_quad8, 0);
27 MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
29 static unsigned int irq[max_num_isa_dev(QUAD8_EXTENT)];
30 module_param_hw_array(irq, uint, irq, NULL, 0);
31 MODULE_PARM_DESC(irq, "ACCES 104-QUAD-8 interrupt line numbers");
33 #define QUAD8_NUM_COUNTERS 8
36 * struct quad8 - device private data structure
37 * @lock: lock to prevent clobbering device states during R/W ops
38 * @counter: instance of the counter_device
39 * @fck_prescaler: array of filter clock prescaler configurations
40 * @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
46 * @irq_trigger: array of current IRQ trigger function configurations
47 * @next_irq_trigger: array of next IRQ trigger function configurations
48 * @synchronous_mode: array of index function synchronous mode configurations
49 * @index_polarity: array of index function polarity configurations
50 * @cable_fault_enable: differential encoder cable status enable configurations
51 * @base: base port address of the device
55 struct counter_device counter;
56 unsigned int fck_prescaler[QUAD8_NUM_COUNTERS];
57 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];
63 unsigned int irq_trigger[QUAD8_NUM_COUNTERS];
64 unsigned int next_irq_trigger[QUAD8_NUM_COUNTERS];
65 unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
66 unsigned int index_polarity[QUAD8_NUM_COUNTERS];
67 unsigned int cable_fault_enable;
71 #define QUAD8_REG_INTERRUPT_STATUS 0x10
72 #define QUAD8_REG_CHAN_OP 0x11
73 #define QUAD8_REG_INDEX_INTERRUPT 0x12
74 #define QUAD8_REG_INDEX_INPUT_LEVELS 0x16
75 #define QUAD8_DIFF_ENCODER_CABLE_STATUS 0x17
76 /* Borrow Toggle flip-flop */
77 #define QUAD8_FLAG_BT BIT(0)
78 /* Carry Toggle flip-flop */
79 #define QUAD8_FLAG_CT BIT(1)
81 #define QUAD8_FLAG_E BIT(4)
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
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
104 /* Transfer Preset Register LSB to FCK Prescaler */
105 #define QUAD8_RLD_PRESET_PSC 0x18
106 #define QUAD8_CHAN_OP_RESET_COUNTERS 0x01
107 #define QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC 0x04
108 #define QUAD8_CMR_QUADRATURE_X1 0x08
109 #define QUAD8_CMR_QUADRATURE_X2 0x10
110 #define QUAD8_CMR_QUADRATURE_X4 0x18
112 static int quad8_signal_read(struct counter_device *counter,
113 struct counter_signal *signal,
114 enum counter_signal_level *level)
116 const struct quad8 *const priv = counter->priv;
119 /* Only Index signal levels can be read */
123 state = inb(priv->base + QUAD8_REG_INDEX_INPUT_LEVELS)
124 & BIT(signal->id - 16);
126 *level = (state) ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW;
131 static int quad8_count_read(struct counter_device *counter,
132 struct counter_count *count, u64 *val)
134 struct quad8 *const priv = counter->priv;
135 const int base_offset = priv->base + 2 * count->id;
139 unsigned long irqflags;
142 flags = inb(base_offset + 1);
143 borrow = flags & QUAD8_FLAG_BT;
144 carry = !!(flags & QUAD8_FLAG_CT);
146 /* Borrow XOR Carry effectively doubles count range */
147 *val = (unsigned long)(borrow ^ carry) << 24;
149 spin_lock_irqsave(&priv->lock, irqflags);
151 /* Reset Byte Pointer; transfer Counter to Output Latch */
152 outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
155 for (i = 0; i < 3; i++)
156 *val |= (unsigned long)inb(base_offset) << (8 * i);
158 spin_unlock_irqrestore(&priv->lock, irqflags);
163 static int quad8_count_write(struct counter_device *counter,
164 struct counter_count *count, u64 val)
166 struct quad8 *const priv = counter->priv;
167 const int base_offset = priv->base + 2 * count->id;
168 unsigned long irqflags;
171 /* Only 24-bit values are supported */
175 spin_lock_irqsave(&priv->lock, irqflags);
177 /* Reset Byte Pointer */
178 outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
180 /* Counter can only be set via Preset Register */
181 for (i = 0; i < 3; i++)
182 outb(val >> (8 * i), base_offset);
184 /* Transfer Preset Register to Counter */
185 outb(QUAD8_CTR_RLD | QUAD8_RLD_PRESET_CNTR, base_offset + 1);
187 /* Reset Byte Pointer */
188 outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
190 /* Set Preset Register back to original value */
191 val = priv->preset[count->id];
192 for (i = 0; i < 3; i++)
193 outb(val >> (8 * i), base_offset);
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);
200 spin_unlock_irqrestore(&priv->lock, irqflags);
205 static const enum counter_function quad8_count_functions_list[] = {
206 COUNTER_FUNCTION_PULSE_DIRECTION,
207 COUNTER_FUNCTION_QUADRATURE_X1_A,
208 COUNTER_FUNCTION_QUADRATURE_X2_A,
209 COUNTER_FUNCTION_QUADRATURE_X4,
212 static int quad8_function_read(struct counter_device *counter,
213 struct counter_count *count,
214 enum counter_function *function)
216 struct quad8 *const priv = counter->priv;
217 const int id = count->id;
218 unsigned long irqflags;
220 spin_lock_irqsave(&priv->lock, irqflags);
222 if (priv->quadrature_mode[id])
223 switch (priv->quadrature_scale[id]) {
225 *function = COUNTER_FUNCTION_QUADRATURE_X1_A;
228 *function = COUNTER_FUNCTION_QUADRATURE_X2_A;
231 *function = COUNTER_FUNCTION_QUADRATURE_X4;
235 *function = COUNTER_FUNCTION_PULSE_DIRECTION;
237 spin_unlock_irqrestore(&priv->lock, irqflags);
242 static int quad8_function_write(struct counter_device *counter,
243 struct counter_count *count,
244 enum counter_function function)
246 struct quad8 *const priv = counter->priv;
247 const int id = count->id;
248 unsigned int *const quadrature_mode = priv->quadrature_mode + id;
249 unsigned int *const scale = priv->quadrature_scale + id;
250 unsigned int *const synchronous_mode = priv->synchronous_mode + id;
251 const int base_offset = priv->base + 2 * id + 1;
252 unsigned long irqflags;
253 unsigned int mode_cfg;
254 unsigned int idr_cfg;
256 spin_lock_irqsave(&priv->lock, irqflags);
258 mode_cfg = priv->count_mode[id] << 1;
259 idr_cfg = priv->index_polarity[id] << 1;
261 if (function == COUNTER_FUNCTION_PULSE_DIRECTION) {
262 *quadrature_mode = 0;
264 /* Quadrature scaling only available in quadrature mode */
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);
274 *quadrature_mode = 1;
277 case COUNTER_FUNCTION_QUADRATURE_X1_A:
279 mode_cfg |= QUAD8_CMR_QUADRATURE_X1;
281 case COUNTER_FUNCTION_QUADRATURE_X2_A:
283 mode_cfg |= QUAD8_CMR_QUADRATURE_X2;
285 case COUNTER_FUNCTION_QUADRATURE_X4:
287 mode_cfg |= QUAD8_CMR_QUADRATURE_X4;
290 /* should never reach this path */
291 spin_unlock_irqrestore(&priv->lock, irqflags);
296 /* Load mode configuration to Counter Mode Register */
297 outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
299 spin_unlock_irqrestore(&priv->lock, irqflags);
304 static int quad8_direction_read(struct counter_device *counter,
305 struct counter_count *count,
306 enum counter_count_direction *direction)
308 const struct quad8 *const priv = counter->priv;
309 unsigned int ud_flag;
310 const unsigned int flag_addr = priv->base + 2 * count->id + 1;
312 /* U/D flag: nonzero = up, zero = down */
313 ud_flag = inb(flag_addr) & QUAD8_FLAG_UD;
315 *direction = (ud_flag) ? COUNTER_COUNT_DIRECTION_FORWARD :
316 COUNTER_COUNT_DIRECTION_BACKWARD;
321 static const enum counter_synapse_action quad8_index_actions_list[] = {
322 COUNTER_SYNAPSE_ACTION_NONE,
323 COUNTER_SYNAPSE_ACTION_RISING_EDGE,
326 static const enum counter_synapse_action quad8_synapse_actions_list[] = {
327 COUNTER_SYNAPSE_ACTION_NONE,
328 COUNTER_SYNAPSE_ACTION_RISING_EDGE,
329 COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
330 COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
333 static int quad8_action_read(struct counter_device *counter,
334 struct counter_count *count,
335 struct counter_synapse *synapse,
336 enum counter_synapse_action *action)
338 struct quad8 *const priv = counter->priv;
340 enum counter_function function;
341 const size_t signal_a_id = count->synapses[0].signal->id;
342 enum counter_count_direction direction;
344 /* Handle Index signals */
345 if (synapse->signal->id >= 16) {
346 if (priv->preset_enable[count->id])
347 *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
349 *action = COUNTER_SYNAPSE_ACTION_NONE;
354 err = quad8_function_read(counter, count, &function);
358 /* Default action mode */
359 *action = COUNTER_SYNAPSE_ACTION_NONE;
361 /* Determine action mode based on current count function mode */
363 case COUNTER_FUNCTION_PULSE_DIRECTION:
364 if (synapse->signal->id == signal_a_id)
365 *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
367 case COUNTER_FUNCTION_QUADRATURE_X1_A:
368 if (synapse->signal->id == signal_a_id) {
369 err = quad8_direction_read(counter, count, &direction);
373 if (direction == COUNTER_COUNT_DIRECTION_FORWARD)
374 *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
376 *action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
379 case COUNTER_FUNCTION_QUADRATURE_X2_A:
380 if (synapse->signal->id == signal_a_id)
381 *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
383 case COUNTER_FUNCTION_QUADRATURE_X4:
384 *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
387 /* should never reach this path */
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,
400 static int quad8_events_configure(struct counter_device *counter)
402 struct quad8 *const priv = counter->priv;
403 unsigned long irq_enabled = 0;
404 unsigned long irqflags;
406 unsigned long ior_cfg;
407 unsigned long base_offset;
409 spin_lock_irqsave(&priv->lock, irqflags);
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)
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];
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);
428 /* Reset next IRQ trigger function configuration */
429 priv->next_irq_trigger[channel] = QUAD8_EVENT_NONE;
431 /* Enable IRQ line */
432 irq_enabled |= BIT(channel);
435 outb(irq_enabled, priv->base + QUAD8_REG_INDEX_INTERRUPT);
437 spin_unlock_irqrestore(&priv->lock, irqflags);
442 static int quad8_watch_validate(struct counter_device *counter,
443 const struct counter_watch *watch)
445 struct quad8 *const priv = counter->priv;
447 if (watch->channel > QUAD8_NUM_COUNTERS - 1)
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)
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)
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)
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)
480 static const struct counter_ops quad8_ops = {
481 .signal_read = quad8_signal_read,
482 .count_read = quad8_count_read,
483 .count_write = quad8_count_write,
484 .function_read = quad8_function_read,
485 .function_write = quad8_function_write,
486 .action_read = quad8_action_read,
487 .events_configure = quad8_events_configure,
488 .watch_validate = quad8_watch_validate,
491 static const char *const quad8_index_polarity_modes[] = {
496 static int quad8_index_polarity_get(struct counter_device *counter,
497 struct counter_signal *signal,
500 const struct quad8 *const priv = counter->priv;
501 const size_t channel_id = signal->id - 16;
503 *index_polarity = priv->index_polarity[channel_id];
508 static int quad8_index_polarity_set(struct counter_device *counter,
509 struct counter_signal *signal,
512 struct quad8 *const priv = counter->priv;
513 const size_t channel_id = signal->id - 16;
514 const int base_offset = priv->base + 2 * channel_id + 1;
515 unsigned long irqflags;
516 unsigned int idr_cfg = index_polarity << 1;
518 spin_lock_irqsave(&priv->lock, irqflags);
520 idr_cfg |= priv->synchronous_mode[channel_id];
522 priv->index_polarity[channel_id] = index_polarity;
524 /* Load Index Control configuration to Index Control Register */
525 outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
527 spin_unlock_irqrestore(&priv->lock, irqflags);
532 static const char *const quad8_synchronous_modes[] = {
537 static int quad8_synchronous_mode_get(struct counter_device *counter,
538 struct counter_signal *signal,
539 u32 *synchronous_mode)
541 const struct quad8 *const priv = counter->priv;
542 const size_t channel_id = signal->id - 16;
544 *synchronous_mode = priv->synchronous_mode[channel_id];
549 static int quad8_synchronous_mode_set(struct counter_device *counter,
550 struct counter_signal *signal,
551 u32 synchronous_mode)
553 struct quad8 *const priv = counter->priv;
554 const size_t channel_id = signal->id - 16;
555 const int base_offset = priv->base + 2 * channel_id + 1;
556 unsigned long irqflags;
557 unsigned int idr_cfg = synchronous_mode;
559 spin_lock_irqsave(&priv->lock, irqflags);
561 idr_cfg |= priv->index_polarity[channel_id] << 1;
563 /* Index function must be non-synchronous in non-quadrature mode */
564 if (synchronous_mode && !priv->quadrature_mode[channel_id]) {
565 spin_unlock_irqrestore(&priv->lock, irqflags);
569 priv->synchronous_mode[channel_id] = synchronous_mode;
571 /* Load Index Control configuration to Index Control Register */
572 outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
574 spin_unlock_irqrestore(&priv->lock, irqflags);
579 static int quad8_count_floor_read(struct counter_device *counter,
580 struct counter_count *count, u64 *floor)
582 /* Only a floor of 0 is supported */
588 static int quad8_count_mode_read(struct counter_device *counter,
589 struct counter_count *count,
590 enum counter_count_mode *cnt_mode)
592 const struct quad8 *const priv = counter->priv;
594 /* Map 104-QUAD-8 count mode to Generic Counter count mode */
595 switch (priv->count_mode[count->id]) {
597 *cnt_mode = COUNTER_COUNT_MODE_NORMAL;
600 *cnt_mode = COUNTER_COUNT_MODE_RANGE_LIMIT;
603 *cnt_mode = COUNTER_COUNT_MODE_NON_RECYCLE;
606 *cnt_mode = COUNTER_COUNT_MODE_MODULO_N;
613 static int quad8_count_mode_write(struct counter_device *counter,
614 struct counter_count *count,
615 enum counter_count_mode cnt_mode)
617 struct quad8 *const priv = counter->priv;
618 unsigned int count_mode;
619 unsigned int mode_cfg;
620 const int base_offset = priv->base + 2 * count->id + 1;
621 unsigned long irqflags;
623 /* Map Generic Counter count mode to 104-QUAD-8 count mode */
625 case COUNTER_COUNT_MODE_NORMAL:
628 case COUNTER_COUNT_MODE_RANGE_LIMIT:
631 case COUNTER_COUNT_MODE_NON_RECYCLE:
634 case COUNTER_COUNT_MODE_MODULO_N:
638 /* should never reach this path */
642 spin_lock_irqsave(&priv->lock, irqflags);
644 priv->count_mode[count->id] = count_mode;
646 /* Set count mode configuration value */
647 mode_cfg = count_mode << 1;
649 /* Add quadrature mode configuration */
650 if (priv->quadrature_mode[count->id])
651 mode_cfg |= (priv->quadrature_scale[count->id] + 1) << 3;
653 /* Load mode configuration to Counter Mode Register */
654 outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
656 spin_unlock_irqrestore(&priv->lock, irqflags);
661 static int quad8_count_enable_read(struct counter_device *counter,
662 struct counter_count *count, u8 *enable)
664 const struct quad8 *const priv = counter->priv;
666 *enable = priv->ab_enable[count->id];
671 static int quad8_count_enable_write(struct counter_device *counter,
672 struct counter_count *count, u8 enable)
674 struct quad8 *const priv = counter->priv;
675 const int base_offset = priv->base + 2 * count->id;
676 unsigned long irqflags;
677 unsigned int ior_cfg;
679 spin_lock_irqsave(&priv->lock, irqflags);
681 priv->ab_enable[count->id] = enable;
683 ior_cfg = enable | priv->preset_enable[count->id] << 1 |
684 priv->irq_trigger[count->id] << 3;
686 /* Load I/O control configuration */
687 outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
689 spin_unlock_irqrestore(&priv->lock, irqflags);
694 static 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"
699 static int quad8_error_noise_get(struct counter_device *counter,
700 struct counter_count *count, u32 *noise_error)
702 const struct quad8 *const priv = counter->priv;
703 const int base_offset = priv->base + 2 * count->id + 1;
705 *noise_error = !!(inb(base_offset) & QUAD8_FLAG_E);
710 static int quad8_count_preset_read(struct counter_device *counter,
711 struct counter_count *count, u64 *preset)
713 const struct quad8 *const priv = counter->priv;
715 *preset = priv->preset[count->id];
720 static void quad8_preset_register_set(struct quad8 *const priv, const int id,
721 const unsigned int preset)
723 const unsigned int base_offset = priv->base + 2 * id;
726 priv->preset[id] = preset;
728 /* Reset Byte Pointer */
729 outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
731 /* Set Preset Register */
732 for (i = 0; i < 3; i++)
733 outb(preset >> (8 * i), base_offset);
736 static int quad8_count_preset_write(struct counter_device *counter,
737 struct counter_count *count, u64 preset)
739 struct quad8 *const priv = counter->priv;
740 unsigned long irqflags;
742 /* Only 24-bit values are supported */
743 if (preset > 0xFFFFFF)
746 spin_lock_irqsave(&priv->lock, irqflags);
748 quad8_preset_register_set(priv, count->id, preset);
750 spin_unlock_irqrestore(&priv->lock, irqflags);
755 static int quad8_count_ceiling_read(struct counter_device *counter,
756 struct counter_count *count, u64 *ceiling)
758 struct quad8 *const priv = counter->priv;
759 unsigned long irqflags;
761 spin_lock_irqsave(&priv->lock, irqflags);
763 /* Range Limit and Modulo-N count modes use preset value as ceiling */
764 switch (priv->count_mode[count->id]) {
767 *ceiling = priv->preset[count->id];
770 /* By default 0x1FFFFFF (25 bits unsigned) is maximum count */
771 *ceiling = 0x1FFFFFF;
775 spin_unlock_irqrestore(&priv->lock, irqflags);
780 static int quad8_count_ceiling_write(struct counter_device *counter,
781 struct counter_count *count, u64 ceiling)
783 struct quad8 *const priv = counter->priv;
784 unsigned long irqflags;
786 /* Only 24-bit values are supported */
787 if (ceiling > 0xFFFFFF)
790 spin_lock_irqsave(&priv->lock, irqflags);
792 /* Range Limit and Modulo-N count modes use preset value as ceiling */
793 switch (priv->count_mode[count->id]) {
796 quad8_preset_register_set(priv, count->id, ceiling);
797 spin_unlock_irqrestore(&priv->lock, irqflags);
801 spin_unlock_irqrestore(&priv->lock, irqflags);
806 static int quad8_count_preset_enable_read(struct counter_device *counter,
807 struct counter_count *count,
810 const struct quad8 *const priv = counter->priv;
812 *preset_enable = !priv->preset_enable[count->id];
817 static int quad8_count_preset_enable_write(struct counter_device *counter,
818 struct counter_count *count,
821 struct quad8 *const priv = counter->priv;
822 const int base_offset = priv->base + 2 * count->id + 1;
823 unsigned long irqflags;
824 unsigned int ior_cfg;
826 /* Preset enable is active low in Input/Output Control register */
827 preset_enable = !preset_enable;
829 spin_lock_irqsave(&priv->lock, irqflags);
831 priv->preset_enable[count->id] = preset_enable;
833 ior_cfg = priv->ab_enable[count->id] | preset_enable << 1 |
834 priv->irq_trigger[count->id] << 3;
836 /* Load I/O control configuration to Input / Output Control Register */
837 outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
839 spin_unlock_irqrestore(&priv->lock, irqflags);
844 static int quad8_signal_cable_fault_read(struct counter_device *counter,
845 struct counter_signal *signal,
848 struct quad8 *const priv = counter->priv;
849 const size_t channel_id = signal->id / 2;
850 unsigned long irqflags;
854 spin_lock_irqsave(&priv->lock, irqflags);
856 disabled = !(priv->cable_fault_enable & BIT(channel_id));
859 spin_unlock_irqrestore(&priv->lock, irqflags);
863 /* Logic 0 = cable fault */
864 status = inb(priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
866 spin_unlock_irqrestore(&priv->lock, irqflags);
868 /* Mask respective channel and invert logic */
869 *cable_fault = !(status & BIT(channel_id));
874 static int quad8_signal_cable_fault_enable_read(struct counter_device *counter,
875 struct counter_signal *signal,
878 const struct quad8 *const priv = counter->priv;
879 const size_t channel_id = signal->id / 2;
881 *enable = !!(priv->cable_fault_enable & BIT(channel_id));
886 static int quad8_signal_cable_fault_enable_write(struct counter_device *counter,
887 struct counter_signal *signal,
890 struct quad8 *const priv = counter->priv;
891 const size_t channel_id = signal->id / 2;
892 unsigned long irqflags;
893 unsigned int cable_fault_enable;
895 spin_lock_irqsave(&priv->lock, irqflags);
898 priv->cable_fault_enable |= BIT(channel_id);
900 priv->cable_fault_enable &= ~BIT(channel_id);
902 /* Enable is active low in Differential Encoder Cable Status register */
903 cable_fault_enable = ~priv->cable_fault_enable;
905 outb(cable_fault_enable, priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
907 spin_unlock_irqrestore(&priv->lock, irqflags);
912 static int quad8_signal_fck_prescaler_read(struct counter_device *counter,
913 struct counter_signal *signal,
916 const struct quad8 *const priv = counter->priv;
918 *prescaler = priv->fck_prescaler[signal->id / 2];
923 static int quad8_signal_fck_prescaler_write(struct counter_device *counter,
924 struct counter_signal *signal,
927 struct quad8 *const priv = counter->priv;
928 const size_t channel_id = signal->id / 2;
929 const int base_offset = priv->base + 2 * channel_id;
930 unsigned long irqflags;
932 spin_lock_irqsave(&priv->lock, irqflags);
934 priv->fck_prescaler[channel_id] = prescaler;
936 /* Reset Byte Pointer */
937 outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
939 /* Set filter clock factor */
940 outb(prescaler, base_offset);
941 outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
944 spin_unlock_irqrestore(&priv->lock, irqflags);
949 static struct counter_comp quad8_signal_ext[] = {
950 COUNTER_COMP_SIGNAL_BOOL("cable_fault", quad8_signal_cable_fault_read,
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)
960 static DEFINE_COUNTER_ENUM(quad8_index_pol_enum, quad8_index_polarity_modes);
961 static DEFINE_COUNTER_ENUM(quad8_synch_mode_enum, quad8_synchronous_modes);
963 static 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),
972 #define QUAD8_QUAD_SIGNAL(_id, _name) { \
975 .ext = quad8_signal_ext, \
976 .num_ext = ARRAY_SIZE(quad8_signal_ext) \
979 #define QUAD8_INDEX_SIGNAL(_id, _name) { \
982 .ext = quad8_index_ext, \
983 .num_ext = ARRAY_SIZE(quad8_index_ext) \
986 static 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")
1013 #define QUAD8_COUNT_SYNAPSES(_id) { \
1015 .actions_list = quad8_synapse_actions_list, \
1016 .num_actions = ARRAY_SIZE(quad8_synapse_actions_list), \
1017 .signal = quad8_signals + 2 * (_id) \
1020 .actions_list = quad8_synapse_actions_list, \
1021 .num_actions = ARRAY_SIZE(quad8_synapse_actions_list), \
1022 .signal = quad8_signals + 2 * (_id) + 1 \
1025 .actions_list = quad8_index_actions_list, \
1026 .num_actions = ARRAY_SIZE(quad8_index_actions_list), \
1027 .signal = quad8_signals + 2 * (_id) + 16 \
1031 static 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)
1038 static 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,
1045 static DEFINE_COUNTER_AVAILABLE(quad8_count_mode_available, quad8_cnt_modes);
1047 static DEFINE_COUNTER_ENUM(quad8_error_noise_enum, quad8_noise_error_states);
1049 static 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),
1064 #define QUAD8_COUNT(_id, _cntname) { \
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) \
1075 static 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")
1086 static irqreturn_t quad8_irq_handler(int irq, void *private)
1088 struct quad8 *const priv = private;
1089 const unsigned long base = priv->base;
1090 unsigned long irq_status;
1091 unsigned long channel;
1094 irq_status = inb(base + QUAD8_REG_INTERRUPT_STATUS);
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;
1103 case QUAD8_EVENT_COMPARE:
1104 event = COUNTER_EVENT_THRESHOLD;
1106 case QUAD8_EVENT_CARRY_BORROW:
1107 event = COUNTER_EVENT_OVERFLOW_UNDERFLOW;
1109 case QUAD8_EVENT_INDEX:
1110 event = COUNTER_EVENT_INDEX;
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);
1119 counter_push_event(&priv->counter, event, channel);
1122 /* Clear pending interrupts on device */
1123 outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base + QUAD8_REG_CHAN_OP);
1128 static int quad8_probe(struct device *dev, unsigned int id)
1132 unsigned int base_offset;
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);
1141 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
1145 /* Initialize Counter device and driver data */
1146 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];
1156 spin_lock_init(&priv->lock);
1158 /* Reset Index/Interrupt Register */
1159 outb(0x00, base[id] + QUAD8_REG_INDEX_INTERRUPT);
1160 /* 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);
1167 /* Reset filter clock factor */
1168 outb(0, base_offset);
1169 outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
1171 /* Reset Byte Pointer */
1172 outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
1173 /* 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);
1186 /* Initialize next IRQ trigger function configuration */
1187 priv->next_irq_trigger[i] = QUAD8_EVENT_NONE;
1189 /* Disable Differential Encoder Cable Status for all channels */
1190 outb(0xFF, base[id] + QUAD8_DIFF_ENCODER_CABLE_STATUS);
1191 /* Enable all counters and enable interrupt function */
1192 outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base[id] + QUAD8_REG_CHAN_OP);
1194 err = devm_request_irq(dev, irq[id], quad8_irq_handler, IRQF_SHARED,
1195 priv->counter.name, priv);
1199 return devm_counter_register(dev, &priv->counter);
1202 static struct isa_driver quad8_driver = {
1203 .probe = quad8_probe,
1205 .name = "104-quad-8"
1209 module_isa_driver(quad8_driver, num_quad8);
1211 MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
1212 MODULE_DESCRIPTION("ACCES 104-QUAD-8 driver");
1213 MODULE_LICENSE("GPL v2");