diff options
Diffstat (limited to 'drivers/counter')
-rw-r--r-- | drivers/counter/interrupt-cnt.c | 17 | ||||
-rw-r--r-- | drivers/counter/microchip-tcb-capture.c | 185 | ||||
-rw-r--r-- | drivers/counter/stm32-timer-cnt.c | 7 | ||||
-rw-r--r-- | drivers/counter/ti-eqep.c | 32 |
4 files changed, 234 insertions, 7 deletions
diff --git a/drivers/counter/interrupt-cnt.c b/drivers/counter/interrupt-cnt.c index 949598d51575..6c0c1d2d7027 100644 --- a/drivers/counter/interrupt-cnt.c +++ b/drivers/counter/interrupt-cnt.c @@ -3,22 +3,25 @@ * Copyright (c) 2021 Pengutronix, Oleksij Rempel <kernel@pengutronix.de> */ +#include <linux/cleanup.h> #include <linux/counter.h> #include <linux/gpio/consumer.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/mod_devicetable.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/platform_device.h> #include <linux/types.h> #define INTERRUPT_CNT_NAME "interrupt-cnt" struct interrupt_cnt_priv { - atomic_t count; + atomic_long_t count; struct gpio_desc *gpio; int irq; bool enabled; + struct mutex lock; struct counter_signal signals; struct counter_synapse synapses; struct counter_count cnts; @@ -29,7 +32,7 @@ static irqreturn_t interrupt_cnt_isr(int irq, void *dev_id) struct counter_device *counter = dev_id; struct interrupt_cnt_priv *priv = counter_priv(counter); - atomic_inc(&priv->count); + atomic_long_inc(&priv->count); counter_push_event(counter, COUNTER_EVENT_CHANGE_OF_STATE, 0); @@ -41,6 +44,8 @@ static int interrupt_cnt_enable_read(struct counter_device *counter, { struct interrupt_cnt_priv *priv = counter_priv(counter); + guard(mutex)(&priv->lock); + *enable = priv->enabled; return 0; @@ -51,6 +56,8 @@ static int interrupt_cnt_enable_write(struct counter_device *counter, { struct interrupt_cnt_priv *priv = counter_priv(counter); + guard(mutex)(&priv->lock); + if (priv->enabled == enable) return 0; @@ -89,7 +96,7 @@ static int interrupt_cnt_read(struct counter_device *counter, { struct interrupt_cnt_priv *priv = counter_priv(counter); - *val = atomic_read(&priv->count); + *val = atomic_long_read(&priv->count); return 0; } @@ -102,7 +109,7 @@ static int interrupt_cnt_write(struct counter_device *counter, if (val != (typeof(priv->count.counter))val) return -ERANGE; - atomic_set(&priv->count, val); + atomic_long_set(&priv->count, val); return 0; } @@ -227,6 +234,8 @@ static int interrupt_cnt_probe(struct platform_device *pdev) if (ret) return ret; + mutex_init(&priv->lock); + ret = devm_counter_add(dev, counter); if (ret < 0) return dev_err_probe(dev, ret, "Failed to add counter\n"); diff --git a/drivers/counter/microchip-tcb-capture.c b/drivers/counter/microchip-tcb-capture.c index c391ac38b990..1a299d1f350b 100644 --- a/drivers/counter/microchip-tcb-capture.c +++ b/drivers/counter/microchip-tcb-capture.c @@ -6,18 +6,24 @@ */ #include <linux/clk.h> #include <linux/counter.h> +#include <linux/interrupt.h> #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/of.h> +#include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/regmap.h> +#include <uapi/linux/counter/microchip-tcb-capture.h> #include <soc/at91/atmel_tcb.h> #define ATMEL_TC_CMR_MASK (ATMEL_TC_LDRA_RISING | ATMEL_TC_LDRB_FALLING | \ ATMEL_TC_ETRGEDG_RISING | ATMEL_TC_LDBDIS | \ ATMEL_TC_LDBSTOP) +#define ATMEL_TC_DEF_IRQS (ATMEL_TC_ETRGS | ATMEL_TC_COVFS | \ + ATMEL_TC_LDRAS | ATMEL_TC_LDRBS | ATMEL_TC_CPCS) + #define ATMEL_TC_QDEN BIT(8) #define ATMEL_TC_POSEN BIT(9) @@ -247,6 +253,112 @@ static int mchp_tc_count_read(struct counter_device *counter, return 0; } +static int mchp_tc_count_cap_read(struct counter_device *counter, + struct counter_count *count, size_t idx, u64 *val) +{ + struct mchp_tc_data *const priv = counter_priv(counter); + u32 cnt; + int ret; + + switch (idx) { + case COUNTER_MCHP_EXCAP_RA: + ret = regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], RA), &cnt); + break; + case COUNTER_MCHP_EXCAP_RB: + ret = regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], RB), &cnt); + break; + default: + return -EINVAL; + } + + if (ret < 0) + return ret; + + *val = cnt; + + return 0; +} + +static int mchp_tc_count_cap_write(struct counter_device *counter, + struct counter_count *count, size_t idx, u64 val) +{ + struct mchp_tc_data *const priv = counter_priv(counter); + int ret; + + if (val > U32_MAX) + return -ERANGE; + + switch (idx) { + case COUNTER_MCHP_EXCAP_RA: + ret = regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], RA), val); + break; + case COUNTER_MCHP_EXCAP_RB: + ret = regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], RB), val); + break; + default: + return -EINVAL; + } + + return ret; +} + +static int mchp_tc_count_compare_read(struct counter_device *counter, struct counter_count *count, + u64 *val) +{ + struct mchp_tc_data *const priv = counter_priv(counter); + u32 cnt; + int ret; + + ret = regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], RC), &cnt); + if (ret < 0) + return ret; + + *val = cnt; + + return 0; +} + +static int mchp_tc_count_compare_write(struct counter_device *counter, struct counter_count *count, + u64 val) +{ + struct mchp_tc_data *const priv = counter_priv(counter); + + if (val > U32_MAX) + return -ERANGE; + + return regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], RC), val); +} + +static DEFINE_COUNTER_ARRAY_CAPTURE(mchp_tc_cnt_cap_array, 2); + +static struct counter_comp mchp_tc_count_ext[] = { + COUNTER_COMP_ARRAY_CAPTURE(mchp_tc_count_cap_read, mchp_tc_count_cap_write, + mchp_tc_cnt_cap_array), + COUNTER_COMP_COMPARE(mchp_tc_count_compare_read, mchp_tc_count_compare_write), +}; + +static int mchp_tc_watch_validate(struct counter_device *counter, + const struct counter_watch *watch) +{ + if (watch->channel == COUNTER_MCHP_EVCHN_CV || watch->channel == COUNTER_MCHP_EVCHN_RA) + switch (watch->event) { + case COUNTER_EVENT_CHANGE_OF_STATE: + case COUNTER_EVENT_OVERFLOW: + case COUNTER_EVENT_CAPTURE: + return 0; + default: + return -EINVAL; + } + + if (watch->channel == COUNTER_MCHP_EVCHN_RB && watch->event == COUNTER_EVENT_CAPTURE) + return 0; + + if (watch->channel == COUNTER_MCHP_EVCHN_RC && watch->event == COUNTER_EVENT_THRESHOLD) + return 0; + + return -EINVAL; +} + static struct counter_count mchp_tc_counts[] = { { .id = 0, @@ -255,6 +367,8 @@ static struct counter_count mchp_tc_counts[] = { .num_functions = ARRAY_SIZE(mchp_tc_count_functions), .synapses = mchp_tc_count_synapses, .num_synapses = ARRAY_SIZE(mchp_tc_count_synapses), + .ext = mchp_tc_count_ext, + .num_ext = ARRAY_SIZE(mchp_tc_count_ext), }, }; @@ -264,7 +378,8 @@ static const struct counter_ops mchp_tc_ops = { .function_read = mchp_tc_count_function_read, .function_write = mchp_tc_count_function_write, .action_read = mchp_tc_count_action_read, - .action_write = mchp_tc_count_action_write + .action_write = mchp_tc_count_action_write, + .watch_validate = mchp_tc_watch_validate, }; static const struct atmel_tcb_config tcb_rm9200_config = { @@ -294,6 +409,65 @@ static const struct of_device_id atmel_tc_of_match[] = { { /* sentinel */ } }; +static irqreturn_t mchp_tc_isr(int irq, void *dev_id) +{ + struct counter_device *const counter = dev_id; + struct mchp_tc_data *const priv = counter_priv(counter); + u32 sr, mask; + + regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], SR), &sr); + regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], IMR), &mask); + + sr &= mask; + if (!(sr & ATMEL_TC_ALL_IRQ)) + return IRQ_NONE; + + if (sr & ATMEL_TC_ETRGS) + counter_push_event(counter, COUNTER_EVENT_CHANGE_OF_STATE, + COUNTER_MCHP_EVCHN_CV); + if (sr & ATMEL_TC_LDRAS) + counter_push_event(counter, COUNTER_EVENT_CAPTURE, + COUNTER_MCHP_EVCHN_RA); + if (sr & ATMEL_TC_LDRBS) + counter_push_event(counter, COUNTER_EVENT_CAPTURE, + COUNTER_MCHP_EVCHN_RB); + if (sr & ATMEL_TC_CPCS) + counter_push_event(counter, COUNTER_EVENT_THRESHOLD, + COUNTER_MCHP_EVCHN_RC); + if (sr & ATMEL_TC_COVFS) + counter_push_event(counter, COUNTER_EVENT_OVERFLOW, + COUNTER_MCHP_EVCHN_CV); + + return IRQ_HANDLED; +} + +static void mchp_tc_irq_remove(void *ptr) +{ + struct mchp_tc_data *priv = ptr; + + regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], IDR), ATMEL_TC_DEF_IRQS); +} + +static int mchp_tc_irq_enable(struct counter_device *const counter, int irq) +{ + struct mchp_tc_data *const priv = counter_priv(counter); + int ret = devm_request_irq(counter->parent, irq, mchp_tc_isr, 0, + dev_name(counter->parent), counter); + + if (ret < 0) + return ret; + + ret = regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], IER), ATMEL_TC_DEF_IRQS); + if (ret < 0) + return ret; + + ret = devm_add_action_or_reset(counter->parent, mchp_tc_irq_remove, priv); + if (ret < 0) + return ret; + + return 0; +} + static void mchp_tc_clk_remove(void *ptr) { clk_disable_unprepare((struct clk *)ptr); @@ -397,6 +571,15 @@ static int mchp_tc_probe(struct platform_device *pdev) counter->num_signals = ARRAY_SIZE(mchp_tc_count_signals); counter->signals = mchp_tc_count_signals; + i = of_irq_get(np->parent, 0); + if (i == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (i > 0) { + ret = mchp_tc_irq_enable(counter, i); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "Failed to set up IRQ"); + } + ret = devm_counter_add(&pdev->dev, counter); if (ret < 0) return dev_err_probe(&pdev->dev, ret, "Failed to add counter\n"); diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c index e75b69476a00..3d3384cbea87 100644 --- a/drivers/counter/stm32-timer-cnt.c +++ b/drivers/counter/stm32-timer-cnt.c @@ -669,12 +669,14 @@ static void stm32_timer_cnt_detect_channels(struct device *dev, dev_dbg(dev, "has %d cc channels\n", priv->nchannels); } -/* encoder supported on TIM1 TIM2 TIM3 TIM4 TIM5 TIM8 */ -#define STM32_TIM_ENCODER_SUPPORTED (BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(7)) +/* encoder supported on TIM1 TIM2 TIM3 TIM4 TIM5 TIM8 TIM20 */ +#define STM32_TIM_ENCODER_SUPPORTED (BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(7) | \ + BIT(19)) static const char * const stm32_timer_trigger_compat[] = { "st,stm32-timer-trigger", "st,stm32h7-timer-trigger", + "st,stm32mp25-timer-trigger", }; static int stm32_timer_cnt_probe_encoder(struct device *dev, @@ -846,6 +848,7 @@ static SIMPLE_DEV_PM_OPS(stm32_timer_cnt_pm_ops, stm32_timer_cnt_suspend, static const struct of_device_id stm32_timer_cnt_of_match[] = { { .compatible = "st,stm32-timer-counter", }, + { .compatible = "st,stm32mp25-timer-counter", }, {}, }; MODULE_DEVICE_TABLE(of, stm32_timer_cnt_of_match); diff --git a/drivers/counter/ti-eqep.c b/drivers/counter/ti-eqep.c index bc586eff0dae..d21c157e531a 100644 --- a/drivers/counter/ti-eqep.c +++ b/drivers/counter/ti-eqep.c @@ -107,6 +107,15 @@ #define QCLR_PCE BIT(1) #define QCLR_INT BIT(0) +#define QEPSTS_UPEVNT BIT(7) +#define QEPSTS_FDF BIT(6) +#define QEPSTS_QDF BIT(5) +#define QEPSTS_QDLF BIT(4) +#define QEPSTS_COEF BIT(3) +#define QEPSTS_CDEF BIT(2) +#define QEPSTS_FIMF BIT(1) +#define QEPSTS_PCEF BIT(0) + /* EQEP Inputs */ enum { TI_EQEP_SIGNAL_QEPA, /* QEPA/XCLK */ @@ -286,6 +295,9 @@ static int ti_eqep_events_configure(struct counter_device *counter) case COUNTER_EVENT_UNDERFLOW: qeint |= QEINT_PCU; break; + case COUNTER_EVENT_DIRECTION_CHANGE: + qeint |= QEINT_QDC; + break; } } @@ -298,6 +310,7 @@ static int ti_eqep_watch_validate(struct counter_device *counter, switch (watch->event) { case COUNTER_EVENT_OVERFLOW: case COUNTER_EVENT_UNDERFLOW: + case COUNTER_EVENT_DIRECTION_CHANGE: if (watch->channel != 0) return -EINVAL; @@ -368,11 +381,27 @@ static int ti_eqep_position_enable_write(struct counter_device *counter, return 0; } +static int ti_eqep_direction_read(struct counter_device *counter, + struct counter_count *count, + enum counter_count_direction *direction) +{ + struct ti_eqep_cnt *priv = counter_priv(counter); + u32 qepsts; + + regmap_read(priv->regmap16, QEPSTS, &qepsts); + + *direction = (qepsts & QEPSTS_QDF) ? COUNTER_COUNT_DIRECTION_FORWARD + : COUNTER_COUNT_DIRECTION_BACKWARD; + + return 0; +} + static struct counter_comp ti_eqep_position_ext[] = { COUNTER_COMP_CEILING(ti_eqep_position_ceiling_read, ti_eqep_position_ceiling_write), COUNTER_COMP_ENABLE(ti_eqep_position_enable_read, ti_eqep_position_enable_write), + COUNTER_COMP_DIRECTION(ti_eqep_direction_read), }; static struct counter_signal ti_eqep_signals[] = { @@ -439,6 +468,9 @@ static irqreturn_t ti_eqep_irq_handler(int irq, void *dev_id) if (qflg & QFLG_PCU) counter_push_event(counter, COUNTER_EVENT_UNDERFLOW, 0); + if (qflg & QFLG_QDC) + counter_push_event(counter, COUNTER_EVENT_DIRECTION_CHANGE, 0); + regmap_write(priv->regmap16, QCLR, qflg); return IRQ_HANDLED; |