diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-11-04 08:21:47 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-11-04 08:21:47 -0700 |
commit | 5c904c66ed4e86c31ac7c033b64274cebed04e0e (patch) | |
tree | 769d366c5e61ffa45d5d8a99c61ae9d5ea39a0a0 /drivers/counter/104-quad-8.c | |
parent | 5cd4dc44b8a0f656100e3b6916cf73b1623299eb (diff) | |
parent | 536de747bc48262225889a533db6650731ab25d3 (diff) | |
download | linux-5c904c66ed4e86c31ac7c033b64274cebed04e0e.tar.gz |
Merge tag 'char-misc-5.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char/misc driver updates from Greg KH:
"Here is the big set of char and misc and other tiny driver subsystem
updates for 5.16-rc1.
Loads of things in here, all of which have been in linux-next for a
while with no reported problems (except for one called out below.)
Included are:
- habanana labs driver updates, including dma_buf usage, reviewed and
acked by the dma_buf maintainers
- iio driver update (going through this tree not staging as they
really do not belong going through that tree anymore)
- counter driver updates
- hwmon driver updates that the counter drivers needed, acked by the
hwmon maintainer
- xillybus driver updates
- binder driver updates
- extcon driver updates
- dma_buf module namespaces added (will cause a build error in arm64
for allmodconfig, but that change is on its way through the drm
tree)
- lkdtm driver updates
- pvpanic driver updates
- phy driver updates
- virt acrn and nitr_enclaves driver updates
- smaller char and misc driver updates"
* tag 'char-misc-5.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (386 commits)
comedi: dt9812: fix DMA buffers on stack
comedi: ni_usb6501: fix NULL-deref in command paths
arm64: errata: Enable TRBE workaround for write to out-of-range address
arm64: errata: Enable workaround for TRBE overwrite in FILL mode
coresight: trbe: Work around write to out of range
coresight: trbe: Make sure we have enough space
coresight: trbe: Add a helper to determine the minimum buffer size
coresight: trbe: Workaround TRBE errata overwrite in FILL mode
coresight: trbe: Add infrastructure for Errata handling
coresight: trbe: Allow driver to choose a different alignment
coresight: trbe: Decouple buffer base from the hardware base
coresight: trbe: Add a helper to pad a given buffer area
coresight: trbe: Add a helper to calculate the trace generated
coresight: trbe: Defer the probe on offline CPUs
coresight: trbe: Fix incorrect access of the sink specific data
coresight: etm4x: Add ETM PID for Kryo-5XX
coresight: trbe: Prohibit trace before disabling TRBE
coresight: trbe: End the AUX handle on truncation
coresight: trbe: Do not truncate buffer on IRQ
coresight: trbe: Fix handling of spurious interrupts
...
Diffstat (limited to 'drivers/counter/104-quad-8.c')
-rw-r--r-- | drivers/counter/104-quad-8.c | 699 |
1 files changed, 401 insertions, 298 deletions
diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c index 0caa60537b14..1cbd60aaed69 100644 --- a/drivers/counter/104-quad-8.c +++ b/drivers/counter/104-quad-8.c @@ -11,11 +11,13 @@ #include <linux/errno.h> #include <linux/io.h> #include <linux/ioport.h> +#include <linux/interrupt.h> #include <linux/isa.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> +#include <linux/spinlock.h> #define QUAD8_EXTENT 32 @@ -24,6 +26,10 @@ static unsigned int num_quad8; module_param_hw_array(base, uint, ioport, &num_quad8, 0); MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses"); +static unsigned int irq[max_num_isa_dev(QUAD8_EXTENT)]; +module_param_hw_array(irq, uint, irq, NULL, 0); +MODULE_PARM_DESC(irq, "ACCES 104-QUAD-8 interrupt line numbers"); + #define QUAD8_NUM_COUNTERS 8 /** @@ -37,13 +43,15 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses"); * @quadrature_scale: array of quadrature mode scale configurations * @ab_enable: array of A and B inputs enable configurations * @preset_enable: array of set_to_preset_on_index attribute configurations + * @irq_trigger: array of current IRQ trigger function configurations + * @next_irq_trigger: array of next IRQ trigger function configurations * @synchronous_mode: array of index function synchronous mode configurations * @index_polarity: array of index function polarity configurations * @cable_fault_enable: differential encoder cable status enable configurations * @base: base port address of the device */ struct quad8 { - struct mutex lock; + spinlock_t lock; struct counter_device counter; unsigned int fck_prescaler[QUAD8_NUM_COUNTERS]; unsigned int preset[QUAD8_NUM_COUNTERS]; @@ -52,13 +60,17 @@ struct quad8 { unsigned int quadrature_scale[QUAD8_NUM_COUNTERS]; unsigned int ab_enable[QUAD8_NUM_COUNTERS]; unsigned int preset_enable[QUAD8_NUM_COUNTERS]; + unsigned int irq_trigger[QUAD8_NUM_COUNTERS]; + unsigned int next_irq_trigger[QUAD8_NUM_COUNTERS]; unsigned int synchronous_mode[QUAD8_NUM_COUNTERS]; unsigned int index_polarity[QUAD8_NUM_COUNTERS]; unsigned int cable_fault_enable; unsigned int base; }; +#define QUAD8_REG_INTERRUPT_STATUS 0x10 #define QUAD8_REG_CHAN_OP 0x11 +#define QUAD8_REG_INDEX_INTERRUPT 0x12 #define QUAD8_REG_INDEX_INPUT_LEVELS 0x16 #define QUAD8_DIFF_ENCODER_CABLE_STATUS 0x17 /* Borrow Toggle flip-flop */ @@ -91,8 +103,8 @@ struct quad8 { #define QUAD8_RLD_CNTR_OUT 0x10 /* Transfer Preset Register LSB to FCK Prescaler */ #define QUAD8_RLD_PRESET_PSC 0x18 -#define QUAD8_CHAN_OP_ENABLE_COUNTERS 0x00 #define QUAD8_CHAN_OP_RESET_COUNTERS 0x01 +#define QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC 0x04 #define QUAD8_CMR_QUADRATURE_X1 0x08 #define QUAD8_CMR_QUADRATURE_X2 0x10 #define QUAD8_CMR_QUADRATURE_X4 0x18 @@ -117,13 +129,14 @@ static int quad8_signal_read(struct counter_device *counter, } static int quad8_count_read(struct counter_device *counter, - struct counter_count *count, unsigned long *val) + struct counter_count *count, u64 *val) { struct quad8 *const priv = counter->priv; const int base_offset = priv->base + 2 * count->id; unsigned int flags; unsigned int borrow; unsigned int carry; + unsigned long irqflags; int i; flags = inb(base_offset + 1); @@ -133,7 +146,7 @@ static int quad8_count_read(struct counter_device *counter, /* Borrow XOR Carry effectively doubles count range */ *val = (unsigned long)(borrow ^ carry) << 24; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); /* Reset Byte Pointer; transfer Counter to Output Latch */ outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT, @@ -142,23 +155,24 @@ static int quad8_count_read(struct counter_device *counter, for (i = 0; i < 3; i++) *val |= (unsigned long)inb(base_offset) << (8 * i); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } static int quad8_count_write(struct counter_device *counter, - struct counter_count *count, unsigned long val) + struct counter_count *count, u64 val) { struct quad8 *const priv = counter->priv; const int base_offset = priv->base + 2 * count->id; + unsigned long irqflags; int i; /* Only 24-bit values are supported */ if (val > 0xFFFFFF) return -ERANGE; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); /* Reset Byte Pointer */ outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); @@ -183,55 +197,51 @@ static int quad8_count_write(struct counter_device *counter, /* Reset Error flag */ outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } -enum quad8_count_function { - QUAD8_COUNT_FUNCTION_PULSE_DIRECTION = 0, - QUAD8_COUNT_FUNCTION_QUADRATURE_X1, - QUAD8_COUNT_FUNCTION_QUADRATURE_X2, - QUAD8_COUNT_FUNCTION_QUADRATURE_X4 -}; - static const enum counter_function quad8_count_functions_list[] = { - [QUAD8_COUNT_FUNCTION_PULSE_DIRECTION] = COUNTER_FUNCTION_PULSE_DIRECTION, - [QUAD8_COUNT_FUNCTION_QUADRATURE_X1] = COUNTER_FUNCTION_QUADRATURE_X1_A, - [QUAD8_COUNT_FUNCTION_QUADRATURE_X2] = COUNTER_FUNCTION_QUADRATURE_X2_A, - [QUAD8_COUNT_FUNCTION_QUADRATURE_X4] = COUNTER_FUNCTION_QUADRATURE_X4 + COUNTER_FUNCTION_PULSE_DIRECTION, + COUNTER_FUNCTION_QUADRATURE_X1_A, + COUNTER_FUNCTION_QUADRATURE_X2_A, + COUNTER_FUNCTION_QUADRATURE_X4, }; -static int quad8_function_get(struct counter_device *counter, - struct counter_count *count, size_t *function) +static int quad8_function_read(struct counter_device *counter, + struct counter_count *count, + enum counter_function *function) { struct quad8 *const priv = counter->priv; const int id = count->id; + unsigned long irqflags; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); if (priv->quadrature_mode[id]) switch (priv->quadrature_scale[id]) { case 0: - *function = QUAD8_COUNT_FUNCTION_QUADRATURE_X1; + *function = COUNTER_FUNCTION_QUADRATURE_X1_A; break; case 1: - *function = QUAD8_COUNT_FUNCTION_QUADRATURE_X2; + *function = COUNTER_FUNCTION_QUADRATURE_X2_A; break; case 2: - *function = QUAD8_COUNT_FUNCTION_QUADRATURE_X4; + *function = COUNTER_FUNCTION_QUADRATURE_X4; break; } else - *function = QUAD8_COUNT_FUNCTION_PULSE_DIRECTION; + *function = COUNTER_FUNCTION_PULSE_DIRECTION; - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } -static int quad8_function_set(struct counter_device *counter, - struct counter_count *count, size_t function) +static int quad8_function_write(struct counter_device *counter, + struct counter_count *count, + enum counter_function function) { struct quad8 *const priv = counter->priv; const int id = count->id; @@ -239,15 +249,16 @@ static int quad8_function_set(struct counter_device *counter, unsigned int *const scale = priv->quadrature_scale + id; unsigned int *const synchronous_mode = priv->synchronous_mode + id; const int base_offset = priv->base + 2 * id + 1; + unsigned long irqflags; unsigned int mode_cfg; unsigned int idr_cfg; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); mode_cfg = priv->count_mode[id] << 1; idr_cfg = priv->index_polarity[id] << 1; - if (function == QUAD8_COUNT_FUNCTION_PULSE_DIRECTION) { + if (function == COUNTER_FUNCTION_PULSE_DIRECTION) { *quadrature_mode = 0; /* Quadrature scaling only available in quadrature mode */ @@ -263,21 +274,21 @@ static int quad8_function_set(struct counter_device *counter, *quadrature_mode = 1; switch (function) { - case QUAD8_COUNT_FUNCTION_QUADRATURE_X1: + case COUNTER_FUNCTION_QUADRATURE_X1_A: *scale = 0; mode_cfg |= QUAD8_CMR_QUADRATURE_X1; break; - case QUAD8_COUNT_FUNCTION_QUADRATURE_X2: + case COUNTER_FUNCTION_QUADRATURE_X2_A: *scale = 1; mode_cfg |= QUAD8_CMR_QUADRATURE_X2; break; - case QUAD8_COUNT_FUNCTION_QUADRATURE_X4: + case COUNTER_FUNCTION_QUADRATURE_X4: *scale = 2; mode_cfg |= QUAD8_CMR_QUADRATURE_X4; break; default: /* should never reach this path */ - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return -EINVAL; } } @@ -285,13 +296,14 @@ static int quad8_function_set(struct counter_device *counter, /* Load mode configuration to Counter Mode Register */ outb(QUAD8_CTR_CMR | mode_cfg, base_offset); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } -static void quad8_direction_get(struct counter_device *counter, - struct counter_count *count, enum counter_count_direction *direction) +static int quad8_direction_read(struct counter_device *counter, + struct counter_count *count, + enum counter_count_direction *direction) { const struct quad8 *const priv = counter->priv; unsigned int ud_flag; @@ -302,76 +314,74 @@ static void quad8_direction_get(struct counter_device *counter, *direction = (ud_flag) ? COUNTER_COUNT_DIRECTION_FORWARD : COUNTER_COUNT_DIRECTION_BACKWARD; -} -enum quad8_synapse_action { - QUAD8_SYNAPSE_ACTION_NONE = 0, - QUAD8_SYNAPSE_ACTION_RISING_EDGE, - QUAD8_SYNAPSE_ACTION_FALLING_EDGE, - QUAD8_SYNAPSE_ACTION_BOTH_EDGES -}; + return 0; +} static const enum counter_synapse_action quad8_index_actions_list[] = { - [QUAD8_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE, - [QUAD8_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE + COUNTER_SYNAPSE_ACTION_NONE, + COUNTER_SYNAPSE_ACTION_RISING_EDGE, }; static const enum counter_synapse_action quad8_synapse_actions_list[] = { - [QUAD8_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE, - [QUAD8_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE, - [QUAD8_SYNAPSE_ACTION_FALLING_EDGE] = COUNTER_SYNAPSE_ACTION_FALLING_EDGE, - [QUAD8_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES + COUNTER_SYNAPSE_ACTION_NONE, + COUNTER_SYNAPSE_ACTION_RISING_EDGE, + COUNTER_SYNAPSE_ACTION_FALLING_EDGE, + COUNTER_SYNAPSE_ACTION_BOTH_EDGES, }; -static int quad8_action_get(struct counter_device *counter, - struct counter_count *count, struct counter_synapse *synapse, - size_t *action) +static int quad8_action_read(struct counter_device *counter, + struct counter_count *count, + struct counter_synapse *synapse, + enum counter_synapse_action *action) { struct quad8 *const priv = counter->priv; int err; - size_t function = 0; + enum counter_function function; const size_t signal_a_id = count->synapses[0].signal->id; enum counter_count_direction direction; /* Handle Index signals */ if (synapse->signal->id >= 16) { if (priv->preset_enable[count->id]) - *action = QUAD8_SYNAPSE_ACTION_RISING_EDGE; + *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE; else - *action = QUAD8_SYNAPSE_ACTION_NONE; + *action = COUNTER_SYNAPSE_ACTION_NONE; return 0; } - err = quad8_function_get(counter, count, &function); + err = quad8_function_read(counter, count, &function); if (err) return err; /* Default action mode */ - *action = QUAD8_SYNAPSE_ACTION_NONE; + *action = COUNTER_SYNAPSE_ACTION_NONE; /* Determine action mode based on current count function mode */ switch (function) { - case QUAD8_COUNT_FUNCTION_PULSE_DIRECTION: + case COUNTER_FUNCTION_PULSE_DIRECTION: if (synapse->signal->id == signal_a_id) - *action = QUAD8_SYNAPSE_ACTION_RISING_EDGE; + *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE; return 0; - case QUAD8_COUNT_FUNCTION_QUADRATURE_X1: + case COUNTER_FUNCTION_QUADRATURE_X1_A: if (synapse->signal->id == signal_a_id) { - quad8_direction_get(counter, count, &direction); + err = quad8_direction_read(counter, count, &direction); + if (err) + return err; if (direction == COUNTER_COUNT_DIRECTION_FORWARD) - *action = QUAD8_SYNAPSE_ACTION_RISING_EDGE; + *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE; else - *action = QUAD8_SYNAPSE_ACTION_FALLING_EDGE; + *action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE; } return 0; - case QUAD8_COUNT_FUNCTION_QUADRATURE_X2: + case COUNTER_FUNCTION_QUADRATURE_X2_A: if (synapse->signal->id == signal_a_id) - *action = QUAD8_SYNAPSE_ACTION_BOTH_EDGES; + *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; return 0; - case QUAD8_COUNT_FUNCTION_QUADRATURE_X4: - *action = QUAD8_SYNAPSE_ACTION_BOTH_EDGES; + case COUNTER_FUNCTION_QUADRATURE_X4: + *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; return 0; default: /* should never reach this path */ @@ -379,13 +389,103 @@ static int quad8_action_get(struct counter_device *counter, } } +enum { + QUAD8_EVENT_NONE = -1, + QUAD8_EVENT_CARRY = 0, + QUAD8_EVENT_COMPARE = 1, + QUAD8_EVENT_CARRY_BORROW = 2, + QUAD8_EVENT_INDEX = 3, +}; + +static int quad8_events_configure(struct counter_device *counter) +{ + struct quad8 *const priv = counter->priv; + unsigned long irq_enabled = 0; + unsigned long irqflags; + size_t channel; + unsigned long ior_cfg; + unsigned long base_offset; + + spin_lock_irqsave(&priv->lock, irqflags); + + /* Enable interrupts for the requested channels, disable for the rest */ + for (channel = 0; channel < QUAD8_NUM_COUNTERS; channel++) { + if (priv->next_irq_trigger[channel] == QUAD8_EVENT_NONE) + continue; + + if (priv->irq_trigger[channel] != priv->next_irq_trigger[channel]) { + /* Save new IRQ function configuration */ + priv->irq_trigger[channel] = priv->next_irq_trigger[channel]; + + /* Load configuration to I/O Control Register */ + ior_cfg = priv->ab_enable[channel] | + priv->preset_enable[channel] << 1 | + priv->irq_trigger[channel] << 3; + base_offset = priv->base + 2 * channel + 1; + outb(QUAD8_CTR_IOR | ior_cfg, base_offset); + } + + /* Reset next IRQ trigger function configuration */ + priv->next_irq_trigger[channel] = QUAD8_EVENT_NONE; + + /* Enable IRQ line */ + irq_enabled |= BIT(channel); + } + + outb(irq_enabled, priv->base + QUAD8_REG_INDEX_INTERRUPT); + + spin_unlock_irqrestore(&priv->lock, irqflags); + + return 0; +} + +static int quad8_watch_validate(struct counter_device *counter, + const struct counter_watch *watch) +{ + struct quad8 *const priv = counter->priv; + + if (watch->channel > QUAD8_NUM_COUNTERS - 1) + return -EINVAL; + + switch (watch->event) { + case COUNTER_EVENT_OVERFLOW: + if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE) + priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_CARRY; + else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_CARRY) + return -EINVAL; + return 0; + case COUNTER_EVENT_THRESHOLD: + if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE) + priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_COMPARE; + else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_COMPARE) + return -EINVAL; + return 0; + case COUNTER_EVENT_OVERFLOW_UNDERFLOW: + if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE) + priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_CARRY_BORROW; + else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_CARRY_BORROW) + return -EINVAL; + return 0; + case COUNTER_EVENT_INDEX: + if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE) + priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_INDEX; + else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_INDEX) + return -EINVAL; + return 0; + default: + return -EINVAL; + } +} + static const struct counter_ops quad8_ops = { .signal_read = quad8_signal_read, .count_read = quad8_count_read, .count_write = quad8_count_write, - .function_get = quad8_function_get, - .function_set = quad8_function_set, - .action_get = quad8_action_get + .function_read = quad8_function_read, + .function_write = quad8_function_write, + .action_read = quad8_action_read, + .events_configure = quad8_events_configure, + .watch_validate = quad8_watch_validate, }; static const char *const quad8_index_polarity_modes[] = { @@ -394,7 +494,8 @@ static const char *const quad8_index_polarity_modes[] = { }; static int quad8_index_polarity_get(struct counter_device *counter, - struct counter_signal *signal, size_t *index_polarity) + struct counter_signal *signal, + u32 *index_polarity) { const struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id - 16; @@ -405,14 +506,16 @@ static int quad8_index_polarity_get(struct counter_device *counter, } static int quad8_index_polarity_set(struct counter_device *counter, - struct counter_signal *signal, size_t index_polarity) + struct counter_signal *signal, + u32 index_polarity) { struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id - 16; const int base_offset = priv->base + 2 * channel_id + 1; + unsigned long irqflags; unsigned int idr_cfg = index_polarity << 1; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); idr_cfg |= priv->synchronous_mode[channel_id]; @@ -421,25 +524,19 @@ static int quad8_index_polarity_set(struct counter_device *counter, /* Load Index Control configuration to Index Control Register */ outb(QUAD8_CTR_IDR | idr_cfg, base_offset); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } -static struct counter_signal_enum_ext quad8_index_pol_enum = { - .items = quad8_index_polarity_modes, - .num_items = ARRAY_SIZE(quad8_index_polarity_modes), - .get = quad8_index_polarity_get, - .set = quad8_index_polarity_set -}; - static const char *const quad8_synchronous_modes[] = { "non-synchronous", "synchronous" }; static int quad8_synchronous_mode_get(struct counter_device *counter, - struct counter_signal *signal, size_t *synchronous_mode) + struct counter_signal *signal, + u32 *synchronous_mode) { const struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id - 16; @@ -450,20 +547,22 @@ static int quad8_synchronous_mode_get(struct counter_device *counter, } static int quad8_synchronous_mode_set(struct counter_device *counter, - struct counter_signal *signal, size_t synchronous_mode) + struct counter_signal *signal, + u32 synchronous_mode) { struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id - 16; const int base_offset = priv->base + 2 * channel_id + 1; + unsigned long irqflags; unsigned int idr_cfg = synchronous_mode; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); idr_cfg |= priv->index_polarity[channel_id] << 1; /* Index function must be non-synchronous in non-quadrature mode */ if (synchronous_mode && !priv->quadrature_mode[channel_id]) { - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return -EINVAL; } @@ -472,27 +571,23 @@ static int quad8_synchronous_mode_set(struct counter_device *counter, /* Load Index Control configuration to Index Control Register */ outb(QUAD8_CTR_IDR | idr_cfg, base_offset); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } -static struct counter_signal_enum_ext quad8_syn_mode_enum = { - .items = quad8_synchronous_modes, - .num_items = ARRAY_SIZE(quad8_synchronous_modes), - .get = quad8_synchronous_mode_get, - .set = quad8_synchronous_mode_set -}; - -static ssize_t quad8_count_floor_read(struct counter_device *counter, - struct counter_count *count, void *private, char *buf) +static int quad8_count_floor_read(struct counter_device *counter, + struct counter_count *count, u64 *floor) { /* Only a floor of 0 is supported */ - return sprintf(buf, "0\n"); + *floor = 0; + + return 0; } -static int quad8_count_mode_get(struct counter_device *counter, - struct counter_count *count, size_t *cnt_mode) +static int quad8_count_mode_read(struct counter_device *counter, + struct counter_count *count, + enum counter_count_mode *cnt_mode) { const struct quad8 *const priv = counter->priv; @@ -515,38 +610,41 @@ static int quad8_count_mode_get(struct counter_device *counter, return 0; } -static int quad8_count_mode_set(struct counter_device *counter, - struct counter_count *count, size_t cnt_mode) +static int quad8_count_mode_write(struct counter_device *counter, + struct counter_count *count, + enum counter_count_mode cnt_mode) { struct quad8 *const priv = counter->priv; + unsigned int count_mode; unsigned int mode_cfg; const int base_offset = priv->base + 2 * count->id + 1; + unsigned long irqflags; /* Map Generic Counter count mode to 104-QUAD-8 count mode */ switch (cnt_mode) { case COUNTER_COUNT_MODE_NORMAL: - cnt_mode = 0; + count_mode = 0; break; case COUNTER_COUNT_MODE_RANGE_LIMIT: - cnt_mode = 1; + count_mode = 1; break; case COUNTER_COUNT_MODE_NON_RECYCLE: - cnt_mode = 2; + count_mode = 2; break; case COUNTER_COUNT_MODE_MODULO_N: - cnt_mode = 3; + count_mode = 3; break; default: /* should never reach this path */ return -EINVAL; } - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); - priv->count_mode[count->id] = cnt_mode; + priv->count_mode[count->id] = count_mode; /* Set count mode configuration value */ - mode_cfg = cnt_mode << 1; + mode_cfg = count_mode << 1; /* Add quadrature mode configuration */ if (priv->quadrature_mode[count->id]) @@ -555,61 +653,42 @@ static int quad8_count_mode_set(struct counter_device *counter, /* Load mode configuration to Counter Mode Register */ outb(QUAD8_CTR_CMR | mode_cfg, base_offset); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } -static struct counter_count_enum_ext quad8_cnt_mode_enum = { - .items = counter_count_mode_str, - .num_items = ARRAY_SIZE(counter_count_mode_str), - .get = quad8_count_mode_get, - .set = quad8_count_mode_set -}; - -static ssize_t quad8_count_direction_read(struct counter_device *counter, - struct counter_count *count, void *priv, char *buf) -{ - enum counter_count_direction dir; - - quad8_direction_get(counter, count, &dir); - - return sprintf(buf, "%s\n", counter_count_direction_str[dir]); -} - -static ssize_t quad8_count_enable_read(struct counter_device *counter, - struct counter_count *count, void *private, char *buf) +static int quad8_count_enable_read(struct counter_device *counter, + struct counter_count *count, u8 *enable) { const struct quad8 *const priv = counter->priv; - return sprintf(buf, "%u\n", priv->ab_enable[count->id]); + *enable = priv->ab_enable[count->id]; + + return 0; } -static ssize_t quad8_count_enable_write(struct counter_device *counter, - struct counter_count *count, void *private, const char *buf, size_t len) +static int quad8_count_enable_write(struct counter_device *counter, + struct counter_count *count, u8 enable) { struct quad8 *const priv = counter->priv; const int base_offset = priv->base + 2 * count->id; - int err; - bool ab_enable; + unsigned long irqflags; unsigned int ior_cfg; - err = kstrtobool(buf, &ab_enable); - if (err) - return err; - - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); - priv->ab_enable[count->id] = ab_enable; + priv->ab_enable[count->id] = enable; - ior_cfg = ab_enable | priv->preset_enable[count->id] << 1; + ior_cfg = enable | priv->preset_enable[count->id] << 1 | + priv->irq_trigger[count->id] << 3; /* Load I/O control configuration */ outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); - return len; + return 0; } static const char *const quad8_noise_error_states[] = { @@ -618,7 +697,7 @@ static const char *const quad8_noise_error_states[] = { }; static int quad8_error_noise_get(struct counter_device *counter, - struct counter_count *count, size_t *noise_error) + struct counter_count *count, u32 *noise_error) { const struct quad8 *const priv = counter->priv; const int base_offset = priv->base + 2 * count->id + 1; @@ -628,18 +707,14 @@ static int quad8_error_noise_get(struct counter_device *counter, return 0; } -static struct counter_count_enum_ext quad8_error_noise_enum = { - .items = quad8_noise_error_states, - .num_items = ARRAY_SIZE(quad8_noise_error_states), - .get = quad8_error_noise_get -}; - -static ssize_t quad8_count_preset_read(struct counter_device *counter, - struct counter_count *count, void *private, char *buf) +static int quad8_count_preset_read(struct counter_device *counter, + struct counter_count *count, u64 *preset) { const struct quad8 *const priv = counter->priv; - return sprintf(buf, "%u\n", priv->preset[count->id]); + *preset = priv->preset[count->id]; + + return 0; } static void quad8_preset_register_set(struct quad8 *const priv, const int id, @@ -658,176 +733,166 @@ static void quad8_preset_register_set(struct quad8 *const priv, const int id, outb(preset >> (8 * i), base_offset); } -static ssize_t quad8_count_preset_write(struct counter_device *counter, - struct counter_count *count, void *private, const char *buf, size_t len) +static int quad8_count_preset_write(struct counter_device *counter, + struct counter_count *count, u64 preset) { struct quad8 *const priv = counter->priv; - unsigned int preset; - int ret; - - ret = kstrtouint(buf, 0, &preset); - if (ret) - return ret; + unsigned long irqflags; /* Only 24-bit values are supported */ if (preset > 0xFFFFFF) return -ERANGE; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); quad8_preset_register_set(priv, count->id, preset); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); - return len; + return 0; } -static ssize_t quad8_count_ceiling_read(struct counter_device *counter, - struct counter_count *count, void *private, char *buf) +static int quad8_count_ceiling_read(struct counter_device *counter, + struct counter_count *count, u64 *ceiling) { struct quad8 *const priv = counter->priv; + unsigned long irqflags; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); /* Range Limit and Modulo-N count modes use preset value as ceiling */ switch (priv->count_mode[count->id]) { case 1: case 3: - mutex_unlock(&priv->lock); - return sprintf(buf, "%u\n", priv->preset[count->id]); + *ceiling = priv->preset[count->id]; + break; + default: + /* By default 0x1FFFFFF (25 bits unsigned) is maximum count */ + *ceiling = 0x1FFFFFF; + break; } - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); - /* By default 0x1FFFFFF (25 bits unsigned) is maximum count */ - return sprintf(buf, "33554431\n"); + return 0; } -static ssize_t quad8_count_ceiling_write(struct counter_device *counter, - struct counter_count *count, void *private, const char *buf, size_t len) +static int quad8_count_ceiling_write(struct counter_device *counter, + struct counter_count *count, u64 ceiling) { struct quad8 *const priv = counter->priv; - unsigned int ceiling; - int ret; - - ret = kstrtouint(buf, 0, &ceiling); - if (ret) - return ret; + unsigned long irqflags; /* Only 24-bit values are supported */ if (ceiling > 0xFFFFFF) return -ERANGE; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); /* Range Limit and Modulo-N count modes use preset value as ceiling */ switch (priv->count_mode[count->id]) { case 1: case 3: quad8_preset_register_set(priv, count->id, ceiling); - mutex_unlock(&priv->lock); - return len; + spin_unlock_irqrestore(&priv->lock, irqflags); + return 0; } - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return -EINVAL; } -static ssize_t quad8_count_preset_enable_read(struct counter_device *counter, - struct counter_count *count, void *private, char *buf) +static int quad8_count_preset_enable_read(struct counter_device *counter, + struct counter_count *count, + u8 *preset_enable) { const struct quad8 *const priv = counter->priv; - return sprintf(buf, "%u\n", !priv->preset_enable[count->id]); + *preset_enable = !priv->preset_enable[count->id]; + + return 0; } -static ssize_t quad8_count_preset_enable_write(struct counter_device *counter, - struct counter_count *count, void *private, const char *buf, size_t len) +static int quad8_count_preset_enable_write(struct counter_device *counter, + struct counter_count *count, + u8 preset_enable) { struct quad8 *const priv = counter->priv; const int base_offset = priv->base + 2 * count->id + 1; - bool preset_enable; - int ret; + unsigned long irqflags; unsigned int ior_cfg; - ret = kstrtobool(buf, &preset_enable); - if (ret) - return ret; - /* Preset enable is active low in Input/Output Control register */ preset_enable = !preset_enable; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); priv->preset_enable[count->id] = preset_enable; - ior_cfg = priv->ab_enable[count->id] | (unsigned int)preset_enable << 1; + ior_cfg = priv->ab_enable[count->id] | preset_enable << 1 | + priv->irq_trigger[count->id] << 3; /* Load I/O control configuration to Input / Output Control Register */ outb(QUAD8_CTR_IOR | ior_cfg, base_offset); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); - return len; + return 0; } -static ssize_t quad8_signal_cable_fault_read(struct counter_device *counter, - struct counter_signal *signal, - void *private, char *buf) +static int quad8_signal_cable_fault_read(struct counter_device *counter, + struct counter_signal *signal, + u8 *cable_fault) { struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id / 2; + unsigned long irqflags; bool disabled; unsigned int status; - unsigned int fault; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); disabled = !(priv->cable_fault_enable & BIT(channel_id)); if (disabled) { - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); return -EINVAL; } /* Logic 0 = cable fault */ status = inb(priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); /* Mask respective channel and invert logic */ - fault = !(status & BIT(channel_id)); + *cable_fault = !(status & BIT(channel_id)); - return sprintf(buf, "%u\n", fault); + return 0; } -static ssize_t quad8_signal_cable_fault_enable_read( - struct counter_device *counter, struct counter_signal *signal, - void *private, char *buf) +static int quad8_signal_cable_fault_enable_read(struct counter_device *counter, + struct counter_signal *signal, + u8 *enable) { const struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id / 2; - const unsigned int enb = !!(priv->cable_fault_enable & BIT(channel_id)); - return sprintf(buf, "%u\n", enb); + *enable = !!(priv->cable_fault_enable & BIT(channel_id)); + + return 0; } -static ssize_t quad8_signal_cable_fault_enable_write( - struct counter_device *counter, struct counter_signal *signal, - void *private, const char *buf, size_t len) +static int quad8_signal_cable_fault_enable_write(struct counter_device *counter, + struct counter_signal *signal, + u8 enable) { struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id / 2; - bool enable; - int ret; + unsigned long irqflags; unsigned int cable_fault_enable; - ret = kstrtobool(buf, &enable); - if (ret) - return ret; - - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); if (enable) priv->cable_fault_enable |= BIT(channel_id); @@ -839,35 +904,32 @@ static ssize_t quad8_signal_cable_fault_enable_write( outb(cable_fault_enable, priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); - return len; + return 0; } -static ssize_t quad8_signal_fck_prescaler_read(struct counter_device *counter, - struct counter_signal *signal, void *private, char *buf) +static int quad8_signal_fck_prescaler_read(struct counter_device *counter, + struct counter_signal *signal, + u8 *prescaler) { const struct quad8 *const priv = counter->priv; - const size_t channel_id = signal->id / 2; - return sprintf(buf, "%u\n", priv->fck_prescaler[channel_id]); + *prescaler = priv->fck_prescaler[signal->id / 2]; + + return 0; } -static ssize_t quad8_signal_fck_prescaler_write(struct counter_device *counter, - struct counter_signal *signal, void *private, const char *buf, - size_t len) +static int quad8_signal_fck_prescaler_write(struct counter_device *counter, + struct counter_signal *signal, + u8 prescaler) { struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id / 2; const int base_offset = priv->base + 2 * channel_id; - u8 prescaler; - int ret; - - ret = kstrtou8(buf, 0, &prescaler); - if (ret) - return ret; + unsigned long irqflags; - mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, irqflags); priv->fck_prescaler[channel_id] = prescaler; @@ -879,33 +941,32 @@ static ssize_t quad8_signal_fck_prescaler_write(struct counter_device *counter, outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC, base_offset + 1); - mutex_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->lock, irqflags); - return len; + return 0; } -static const struct counter_signal_ext quad8_signal_ext[] = { - { - .name = "cable_fault", - .read = quad8_signal_cable_fault_read - }, - { - .name = "cable_fault_enable", - .read = quad8_signal_cable_fault_enable_read, - .write = quad8_signal_cable_fault_enable_write - }, - { - .name = "filter_clock_prescaler", - .read = quad8_signal_fck_prescaler_read, - .write = quad8_signal_fck_prescaler_write - } +static struct counter_comp quad8_signal_ext[] = { + COUNTER_COMP_SIGNAL_BOOL("cable_fault", quad8_signal_cable_fault_read, + NULL), + COUNTER_COMP_SIGNAL_BOOL("cable_fault_enable", + quad8_signal_cable_fault_enable_read, + quad8_signal_cable_fault_enable_write), + COUNTER_COMP_SIGNAL_U8("filter_clock_prescaler", + quad8_signal_fck_prescaler_read, + quad8_signal_fck_prescaler_write) }; -static const struct counter_signal_ext quad8_index_ext[] = { - COUNTER_SIGNAL_ENUM("index_polarity", &quad8_index_pol_enum), - COUNTER_SIGNAL_ENUM_AVAILABLE("index_polarity", &quad8_index_pol_enum), - COUNTER_SIGNAL_ENUM("synchronous_mode", &quad8_syn_mode_enum), - COUNTER_SIGNAL_ENUM_AVAILABLE("synchronous_mode", &quad8_syn_mode_enum) +static DEFINE_COUNTER_ENUM(quad8_index_pol_enum, quad8_index_polarity_modes); +static DEFINE_COUNTER_ENUM(quad8_synch_mode_enum, quad8_synchronous_modes); + +static struct counter_comp quad8_index_ext[] = { + COUNTER_COMP_SIGNAL_ENUM("index_polarity", quad8_index_polarity_get, + quad8_index_polarity_set, + quad8_index_pol_enum), + COUNTER_COMP_SIGNAL_ENUM("synchronous_mode", quad8_synchronous_mode_get, + quad8_synchronous_mode_set, + quad8_synch_mode_enum), }; #define QUAD8_QUAD_SIGNAL(_id, _name) { \ @@ -974,39 +1035,30 @@ static struct counter_synapse quad8_count_synapses[][3] = { QUAD8_COUNT_SYNAPSES(6), QUAD8_COUNT_SYNAPSES(7) }; -static const struct counter_count_ext quad8_count_ext[] = { - { - .name = "ceiling", - .read = quad8_count_ceiling_read, - .write = quad8_count_ceiling_write - }, - { - .name = "floor", - .read = quad8_count_floor_read - }, - COUNTER_COUNT_ENUM("count_mode", &quad8_cnt_mode_enum), - COUNTER_COUNT_ENUM_AVAILABLE("count_mode", &quad8_cnt_mode_enum), - { - .name = "direction", - .read = quad8_count_direction_read - }, - { - .name = "enable", - .read = quad8_count_enable_read, - .write = quad8_count_enable_write - }, - COUNTER_COUNT_ENUM("error_noise", &quad8_error_noise_enum), - COUNTER_COUNT_ENUM_AVAILABLE("error_noise", &quad8_error_noise_enum), - { - .name = "preset", - .read = quad8_count_preset_read, - .write = quad8_count_preset_write - }, - { - .name = "preset_enable", - .read = quad8_count_preset_enable_read, - .write = quad8_count_preset_enable_write - } +static const enum counter_count_mode quad8_cnt_modes[] = { + COUNTER_COUNT_MODE_NORMAL, + COUNTER_COUNT_MODE_RANGE_LIMIT, + COUNTER_COUNT_MODE_NON_RECYCLE, + COUNTER_COUNT_MODE_MODULO_N, +}; + +static DEFINE_COUNTER_AVAILABLE(quad8_count_mode_available, quad8_cnt_modes); + +static DEFINE_COUNTER_ENUM(quad8_error_noise_enum, quad8_noise_error_states); + +static struct counter_comp quad8_count_ext[] = { + COUNTER_COMP_CEILING(quad8_count_ceiling_read, + quad8_count_ceiling_write), + COUNTER_COMP_FLOOR(quad8_count_floor_read, NULL), + COUNTER_COMP_COUNT_MODE(quad8_count_mode_read, quad8_count_mode_write, + quad8_count_mode_available), + COUNTER_COMP_DIRECTION(quad8_direction_read), + COUNTER_COMP_ENABLE(quad8_count_enable_read, quad8_count_enable_write), + COUNTER_COMP_COUNT_ENUM("error_noise", quad8_error_noise_get, NULL, + quad8_error_noise_enum), + COUNTER_COMP_PRESET(quad8_count_preset_read, quad8_count_preset_write), + COUNTER_COMP_PRESET_ENABLE(quad8_count_preset_enable_read, + quad8_count_preset_enable_write), }; #define QUAD8_COUNT(_id, _cntname) { \ @@ -1031,11 +1083,54 @@ static struct counter_count quad8_counts[] = { QUAD8_COUNT(7, "Channel 8 Count") }; +static irqreturn_t quad8_irq_handler(int irq, void *private) +{ + struct quad8 *const priv = private; + const unsigned long base = priv->base; + unsigned long irq_status; + unsigned long channel; + u8 event; + + irq_status = inb(base + QUAD8_REG_INTERRUPT_STATUS); + if (!irq_status) + return IRQ_NONE; + + for_each_set_bit(channel, &irq_status, QUAD8_NUM_COUNTERS) { + switch (priv->irq_trigger[channel]) { + case QUAD8_EVENT_CARRY: + event = COUNTER_EVENT_OVERFLOW; + break; + case QUAD8_EVENT_COMPARE: + event = COUNTER_EVENT_THRESHOLD; + break; + case QUAD8_EVENT_CARRY_BORROW: + event = COUNTER_EVENT_OVERFLOW_UNDERFLOW; + break; + case QUAD8_EVENT_INDEX: + event = COUNTER_EVENT_INDEX; + break; + default: + /* should never reach this path */ + WARN_ONCE(true, "invalid interrupt trigger function %u configured for channel %lu\n", + priv->irq_trigger[channel], channel); + continue; + } + + counter_push_event(&priv->counter, event, channel); + } + + /* Clear pending interrupts on device */ + outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base + QUAD8_REG_CHAN_OP); + + return IRQ_HANDLED; +} + static int quad8_probe(struct device *dev, unsigned int id) { struct quad8 *priv; int i, j; unsigned int base_offset; + int err; if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) { dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", @@ -1058,9 +1153,10 @@ static int quad8_probe(struct device *dev, unsigned int id) priv->counter.priv = priv; priv->base = base[id]; - /* Initialize mutex */ - mutex_init(&priv->lock); + spin_lock_init(&priv->lock); + /* Reset Index/Interrupt Register */ + outb(0x00, base[id] + QUAD8_REG_INDEX_INTERRUPT); /* Reset all counters and disable interrupt function */ outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP); /* Set initial configuration for all counters */ @@ -1087,11 +1183,18 @@ static int quad8_probe(struct device *dev, unsigned int id) outb(QUAD8_CTR_IOR, base_offset + 1); /* Disable index function; negative index polarity */ outb(QUAD8_CTR_IDR, base_offset + 1); + /* Initialize next IRQ trigger function configuration */ + priv->next_irq_trigger[i] = QUAD8_EVENT_NONE; } /* Disable Differential Encoder Cable Status for all channels */ outb(0xFF, base[id] + QUAD8_DIFF_ENCODER_CABLE_STATUS); - /* Enable all counters */ - outb(QUAD8_CHAN_OP_ENABLE_COUNTERS, base[id] + QUAD8_REG_CHAN_OP); + /* Enable all counters and enable interrupt function */ + outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base[id] + QUAD8_REG_CHAN_OP); + + err = devm_request_irq(dev, irq[id], quad8_irq_handler, IRQF_SHARED, + priv->counter.name, priv); + if (err) + return err; return devm_counter_register(dev, &priv->counter); } |