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