diff options
Diffstat (limited to 'drivers/counter')
-rw-r--r-- | drivers/counter/104-quad-8.c | 4 | ||||
-rw-r--r-- | drivers/counter/Kconfig | 2 | ||||
-rw-r--r-- | drivers/counter/counter-chrdev.c | 3 | ||||
-rw-r--r-- | drivers/counter/counter-core.c | 18 | ||||
-rw-r--r-- | drivers/counter/ftm-quaddec.c | 4 | ||||
-rw-r--r-- | drivers/counter/i8254.c | 6 | ||||
-rw-r--r-- | drivers/counter/intel-qep.c | 12 | ||||
-rw-r--r-- | drivers/counter/interrupt-cnt.c | 2 | ||||
-rw-r--r-- | drivers/counter/microchip-tcb-capture.c | 2 | ||||
-rw-r--r-- | drivers/counter/rz-mtu3-cnt.c | 2 | ||||
-rw-r--r-- | drivers/counter/stm32-lptimer-cnt.c | 2 | ||||
-rw-r--r-- | drivers/counter/stm32-timer-cnt.c | 480 | ||||
-rw-r--r-- | drivers/counter/ti-ecap-capture.c | 15 | ||||
-rw-r--r-- | drivers/counter/ti-eqep.c | 143 |
14 files changed, 621 insertions, 74 deletions
diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c index ed1f57511955..ce81fc4e1ae7 100644 --- a/drivers/counter/104-quad-8.c +++ b/drivers/counter/104-quad-8.c @@ -22,7 +22,7 @@ #include <linux/spinlock.h> #include <linux/types.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #define QUAD8_EXTENT 32 @@ -1360,4 +1360,4 @@ module_isa_driver_with_irq(quad8_driver, num_quad8, num_irq); MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); MODULE_DESCRIPTION("ACCES 104-QUAD-8 driver"); MODULE_LICENSE("GPL v2"); -MODULE_IMPORT_NS(COUNTER); +MODULE_IMPORT_NS("COUNTER"); diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index 497bc05dca4d..d30d22dfe577 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -138,7 +138,7 @@ config TI_ECAP_CAPTURE config TI_EQEP tristate "TI eQEP counter driver" - depends on (SOC_AM33XX || COMPILE_TEST) + depends on SOC_AM33XX || ARCH_K3 || COMPILE_TEST select REGMAP_MMIO help Select this option to enable the Texas Instruments Enhanced Quadrature diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c index afc94d0062b1..23fdf0caf712 100644 --- a/drivers/counter/counter-chrdev.c +++ b/drivers/counter/counter-chrdev.c @@ -454,7 +454,6 @@ out_unlock: static const struct file_operations counter_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .read = counter_chrdev_read, .poll = counter_chrdev_poll, .unlocked_ioctl = counter_chrdev_ioctl, @@ -673,4 +672,4 @@ exit_early: if (copied) wake_up_poll(&counter->events_wait, EPOLLIN); } -EXPORT_SYMBOL_NS_GPL(counter_push_event, COUNTER); +EXPORT_SYMBOL_NS_GPL(counter_push_event, "COUNTER"); diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c index 3f24481fc04a..50bd30ba3d03 100644 --- a/drivers/counter/counter-core.c +++ b/drivers/counter/counter-core.c @@ -49,12 +49,12 @@ static void counter_device_release(struct device *dev) kfree(container_of(counter, struct counter_device_allochelper, counter)); } -static struct device_type counter_device_type = { +static const struct device_type counter_device_type = { .name = "counter_device", .release = counter_device_release, }; -static struct bus_type counter_bus_type = { +static const struct bus_type counter_bus_type = { .name = "counter", .dev_name = "counter", }; @@ -74,7 +74,7 @@ void *counter_priv(const struct counter_device *const counter) return &ch->privdata; } -EXPORT_SYMBOL_NS_GPL(counter_priv, COUNTER); +EXPORT_SYMBOL_NS_GPL(counter_priv, "COUNTER"); /** * counter_alloc - allocate a counter_device @@ -134,13 +134,13 @@ err_ida_alloc: return NULL; } -EXPORT_SYMBOL_NS_GPL(counter_alloc, COUNTER); +EXPORT_SYMBOL_NS_GPL(counter_alloc, "COUNTER"); void counter_put(struct counter_device *counter) { put_device(&counter->dev); } -EXPORT_SYMBOL_NS_GPL(counter_put, COUNTER); +EXPORT_SYMBOL_NS_GPL(counter_put, "COUNTER"); /** * counter_add - complete registration of a counter @@ -167,7 +167,7 @@ int counter_add(struct counter_device *counter) /* implies device_add(dev) */ return cdev_device_add(&counter->chrdev, dev); } -EXPORT_SYMBOL_NS_GPL(counter_add, COUNTER); +EXPORT_SYMBOL_NS_GPL(counter_add, "COUNTER"); /** * counter_unregister - unregister Counter from the system @@ -189,7 +189,7 @@ void counter_unregister(struct counter_device *const counter) mutex_unlock(&counter->ops_exist_lock); } -EXPORT_SYMBOL_NS_GPL(counter_unregister, COUNTER); +EXPORT_SYMBOL_NS_GPL(counter_unregister, "COUNTER"); static void devm_counter_release(void *counter) { @@ -224,7 +224,7 @@ struct counter_device *devm_counter_alloc(struct device *dev, size_t sizeof_priv return counter; } -EXPORT_SYMBOL_NS_GPL(devm_counter_alloc, COUNTER); +EXPORT_SYMBOL_NS_GPL(devm_counter_alloc, "COUNTER"); /** * devm_counter_add - complete registration of a counter @@ -245,7 +245,7 @@ int devm_counter_add(struct device *dev, return devm_add_action_or_reset(dev, devm_counter_release, counter); } -EXPORT_SYMBOL_NS_GPL(devm_counter_add, COUNTER); +EXPORT_SYMBOL_NS_GPL(devm_counter_add, "COUNTER"); #define COUNTER_DEV_MAX 256 diff --git a/drivers/counter/ftm-quaddec.c b/drivers/counter/ftm-quaddec.c index aea6622a9b13..c47741292ae1 100644 --- a/drivers/counter/ftm-quaddec.c +++ b/drivers/counter/ftm-quaddec.c @@ -311,6 +311,7 @@ static const struct of_device_id ftm_quaddec_match[] = { { .compatible = "fsl,ftm-quaddec" }, {}, }; +MODULE_DEVICE_TABLE(of, ftm_quaddec_match); static struct platform_driver ftm_quaddec_driver = { .driver = { @@ -322,7 +323,8 @@ static struct platform_driver ftm_quaddec_driver = { module_platform_driver(ftm_quaddec_driver); +MODULE_DESCRIPTION("Flex Timer Module Quadrature decoder"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kjeld Flarup <kfa@deif.com>"); MODULE_AUTHOR("Patrick Havelange <patrick.havelange@essensium.com>"); -MODULE_IMPORT_NS(COUNTER); +MODULE_IMPORT_NS("COUNTER"); diff --git a/drivers/counter/i8254.c b/drivers/counter/i8254.c index c41e4fdc9601..95ad928725ec 100644 --- a/drivers/counter/i8254.c +++ b/drivers/counter/i8254.c @@ -15,7 +15,7 @@ #include <linux/mutex.h> #include <linux/regmap.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #define I8254_COUNTER_REG(_counter) (_counter) #define I8254_CONTROL_REG 0x3 @@ -439,9 +439,9 @@ int devm_i8254_regmap_register(struct device *const dev, return 0; } -EXPORT_SYMBOL_NS_GPL(devm_i8254_regmap_register, I8254); +EXPORT_SYMBOL_NS_GPL(devm_i8254_regmap_register, "I8254"); MODULE_AUTHOR("William Breathitt Gray"); MODULE_DESCRIPTION("Intel 8254 Programmable Interval Timer"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(COUNTER); +MODULE_IMPORT_NS("COUNTER"); diff --git a/drivers/counter/intel-qep.c b/drivers/counter/intel-qep.c index af5942e66f7d..c49c178056f4 100644 --- a/drivers/counter/intel-qep.c +++ b/drivers/counter/intel-qep.c @@ -408,13 +408,9 @@ static int intel_qep_probe(struct pci_dev *pci, const struct pci_device_id *id) pci_set_master(pci); - ret = pcim_iomap_regions(pci, BIT(0), pci_name(pci)); - if (ret) - return ret; - - regs = pcim_iomap_table(pci)[0]; - if (!regs) - return -ENOMEM; + regs = pcim_iomap_region(pci, 0, pci_name(pci)); + if (IS_ERR(regs)) + return PTR_ERR(regs); qep->dev = dev; qep->regs = regs; @@ -523,4 +519,4 @@ MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>"); MODULE_AUTHOR("Raymond Tan <raymond.tan@intel.com>"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Intel Quadrature Encoder Peripheral driver"); -MODULE_IMPORT_NS(COUNTER); +MODULE_IMPORT_NS("COUNTER"); diff --git a/drivers/counter/interrupt-cnt.c b/drivers/counter/interrupt-cnt.c index 229473855c5b..949598d51575 100644 --- a/drivers/counter/interrupt-cnt.c +++ b/drivers/counter/interrupt-cnt.c @@ -253,4 +253,4 @@ MODULE_ALIAS("platform:interrupt-counter"); MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>"); MODULE_DESCRIPTION("Interrupt counter driver"); MODULE_LICENSE("GPL v2"); -MODULE_IMPORT_NS(COUNTER); +MODULE_IMPORT_NS("COUNTER"); diff --git a/drivers/counter/microchip-tcb-capture.c b/drivers/counter/microchip-tcb-capture.c index b3e615cbd2ca..2f096a5b973d 100644 --- a/drivers/counter/microchip-tcb-capture.c +++ b/drivers/counter/microchip-tcb-capture.c @@ -403,4 +403,4 @@ module_platform_driver(mchp_tc_driver); MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>"); MODULE_DESCRIPTION("Microchip TCB Capture driver"); MODULE_LICENSE("GPL v2"); -MODULE_IMPORT_NS(COUNTER); +MODULE_IMPORT_NS("COUNTER"); diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c index ee821493b166..e755d54dfece 100644 --- a/drivers/counter/rz-mtu3-cnt.c +++ b/drivers/counter/rz-mtu3-cnt.c @@ -903,4 +903,4 @@ MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>"); MODULE_ALIAS("platform:rz-mtu3-counter"); MODULE_DESCRIPTION("Renesas RZ/G2L MTU3a counter driver"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(COUNTER); +MODULE_IMPORT_NS("COUNTER"); diff --git a/drivers/counter/stm32-lptimer-cnt.c b/drivers/counter/stm32-lptimer-cnt.c index 8439755559b2..cf73f65baf60 100644 --- a/drivers/counter/stm32-lptimer-cnt.c +++ b/drivers/counter/stm32-lptimer-cnt.c @@ -520,4 +520,4 @@ MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>"); MODULE_ALIAS("platform:stm32-lptimer-counter"); MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver"); MODULE_LICENSE("GPL v2"); -MODULE_IMPORT_NS(COUNTER); +MODULE_IMPORT_NS("COUNTER"); diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c index 6206d2dc3d47..e75b69476a00 100644 --- a/drivers/counter/stm32-timer-cnt.c +++ b/drivers/counter/stm32-timer-cnt.c @@ -8,9 +8,11 @@ * */ #include <linux/counter.h> +#include <linux/interrupt.h> #include <linux/mfd/stm32-timers.h> #include <linux/mod_devicetable.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> #include <linux/types.h> @@ -21,6 +23,12 @@ #define TIM_CCER_MASK (TIM_CCER_CC1P | TIM_CCER_CC1NP | \ TIM_CCER_CC2P | TIM_CCER_CC2NP) +#define STM32_CH1_SIG 0 +#define STM32_CH2_SIG 1 +#define STM32_CLOCK_SIG 2 +#define STM32_CH3_SIG 3 +#define STM32_CH4_SIG 4 + struct stm32_timer_regs { u32 cr1; u32 cnt; @@ -34,6 +42,11 @@ struct stm32_timer_cnt { u32 max_arr; bool enabled; struct stm32_timer_regs bak; + bool has_encoder; + unsigned int nchannels; + unsigned int nr_irqs; + spinlock_t lock; /* protects nb_ovf */ + u64 nb_ovf; }; static const enum counter_function stm32_count_functions[] = { @@ -107,12 +120,18 @@ static int stm32_count_function_write(struct counter_device *counter, sms = TIM_SMCR_SMS_SLAVE_MODE_DISABLED; break; case COUNTER_FUNCTION_QUADRATURE_X2_A: + if (!priv->has_encoder) + return -EOPNOTSUPP; sms = TIM_SMCR_SMS_ENCODER_MODE_1; break; case COUNTER_FUNCTION_QUADRATURE_X2_B: + if (!priv->has_encoder) + return -EOPNOTSUPP; sms = TIM_SMCR_SMS_ENCODER_MODE_2; break; case COUNTER_FUNCTION_QUADRATURE_X4: + if (!priv->has_encoder) + return -EOPNOTSUPP; sms = TIM_SMCR_SMS_ENCODER_MODE_3; break; default: @@ -195,11 +214,17 @@ static int stm32_count_enable_write(struct counter_device *counter, { struct stm32_timer_cnt *const priv = counter_priv(counter); u32 cr1; + int ret; if (enable) { regmap_read(priv->regmap, TIM_CR1, &cr1); - if (!(cr1 & TIM_CR1_CEN)) - clk_enable(priv->clk); + if (!(cr1 & TIM_CR1_CEN)) { + ret = clk_enable(priv->clk); + if (ret) { + dev_err(counter->parent, "Cannot enable clock %d\n", ret); + return ret; + } + } regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN); @@ -216,11 +241,108 @@ static int stm32_count_enable_write(struct counter_device *counter, return 0; } +static int stm32_count_prescaler_read(struct counter_device *counter, + struct counter_count *count, u64 *prescaler) +{ + struct stm32_timer_cnt *const priv = counter_priv(counter); + u32 psc; + + regmap_read(priv->regmap, TIM_PSC, &psc); + + *prescaler = psc + 1; + + return 0; +} + +static int stm32_count_prescaler_write(struct counter_device *counter, + struct counter_count *count, u64 prescaler) +{ + struct stm32_timer_cnt *const priv = counter_priv(counter); + u32 psc; + + if (!prescaler || prescaler > MAX_TIM_PSC + 1) + return -ERANGE; + + psc = prescaler - 1; + + return regmap_write(priv->regmap, TIM_PSC, psc); +} + +static int stm32_count_cap_read(struct counter_device *counter, + struct counter_count *count, + size_t ch, u64 *cap) +{ + struct stm32_timer_cnt *const priv = counter_priv(counter); + u32 ccrx; + + if (ch >= priv->nchannels) + return -EOPNOTSUPP; + + switch (ch) { + case 0: + regmap_read(priv->regmap, TIM_CCR1, &ccrx); + break; + case 1: + regmap_read(priv->regmap, TIM_CCR2, &ccrx); + break; + case 2: + regmap_read(priv->regmap, TIM_CCR3, &ccrx); + break; + case 3: + regmap_read(priv->regmap, TIM_CCR4, &ccrx); + break; + default: + return -EINVAL; + } + + dev_dbg(counter->parent, "CCR%zu: 0x%08x\n", ch + 1, ccrx); + + *cap = ccrx; + + return 0; +} + +static int stm32_count_nb_ovf_read(struct counter_device *counter, + struct counter_count *count, u64 *val) +{ + struct stm32_timer_cnt *const priv = counter_priv(counter); + unsigned long irqflags; + + spin_lock_irqsave(&priv->lock, irqflags); + *val = priv->nb_ovf; + spin_unlock_irqrestore(&priv->lock, irqflags); + + return 0; +} + +static int stm32_count_nb_ovf_write(struct counter_device *counter, + struct counter_count *count, u64 val) +{ + struct stm32_timer_cnt *const priv = counter_priv(counter); + unsigned long irqflags; + + spin_lock_irqsave(&priv->lock, irqflags); + priv->nb_ovf = val; + spin_unlock_irqrestore(&priv->lock, irqflags); + + return 0; +} + +static DEFINE_COUNTER_ARRAY_CAPTURE(stm32_count_cap_array, 4); + static struct counter_comp stm32_count_ext[] = { COUNTER_COMP_DIRECTION(stm32_count_direction_read), COUNTER_COMP_ENABLE(stm32_count_enable_read, stm32_count_enable_write), COUNTER_COMP_CEILING(stm32_count_ceiling_read, stm32_count_ceiling_write), + COUNTER_COMP_COUNT_U64("prescaler", stm32_count_prescaler_read, + stm32_count_prescaler_write), + COUNTER_COMP_ARRAY_CAPTURE(stm32_count_cap_read, NULL, stm32_count_cap_array), + COUNTER_COMP_COUNT_U64("num_overflows", stm32_count_nb_ovf_read, stm32_count_nb_ovf_write), +}; + +static const enum counter_synapse_action stm32_clock_synapse_actions[] = { + COUNTER_SYNAPSE_ACTION_RISING_EDGE, }; static const enum counter_synapse_action stm32_synapse_actions[] = { @@ -243,25 +365,152 @@ static int stm32_action_read(struct counter_device *counter, switch (function) { case COUNTER_FUNCTION_INCREASE: /* counts on internal clock when CEN=1 */ - *action = COUNTER_SYNAPSE_ACTION_NONE; + if (synapse->signal->id == STM32_CLOCK_SIG) + *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE; + else + *action = COUNTER_SYNAPSE_ACTION_NONE; return 0; case COUNTER_FUNCTION_QUADRATURE_X2_A: /* counts up/down on TI1FP1 edge depending on TI2FP2 level */ - if (synapse->signal->id == count->synapses[0].signal->id) + if (synapse->signal->id == STM32_CH1_SIG) *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; else *action = COUNTER_SYNAPSE_ACTION_NONE; return 0; case COUNTER_FUNCTION_QUADRATURE_X2_B: /* counts up/down on TI2FP2 edge depending on TI1FP1 level */ - if (synapse->signal->id == count->synapses[1].signal->id) + if (synapse->signal->id == STM32_CH2_SIG) *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; else *action = COUNTER_SYNAPSE_ACTION_NONE; return 0; case COUNTER_FUNCTION_QUADRATURE_X4: /* counts up/down on both TI1FP1 and TI2FP2 edges */ - *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; + if (synapse->signal->id == STM32_CH1_SIG || synapse->signal->id == STM32_CH2_SIG) + *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; + else + *action = COUNTER_SYNAPSE_ACTION_NONE; + return 0; + default: + return -EINVAL; + } +} + +struct stm32_count_cc_regs { + u32 ccmr_reg; + u32 ccmr_mask; + u32 ccmr_bits; + u32 ccer_bits; +}; + +static const struct stm32_count_cc_regs stm32_cc[] = { + { TIM_CCMR1, TIM_CCMR_CC1S, TIM_CCMR_CC1S_TI1, + TIM_CCER_CC1E | TIM_CCER_CC1P | TIM_CCER_CC1NP }, + { TIM_CCMR1, TIM_CCMR_CC2S, TIM_CCMR_CC2S_TI2, + TIM_CCER_CC2E | TIM_CCER_CC2P | TIM_CCER_CC2NP }, + { TIM_CCMR2, TIM_CCMR_CC3S, TIM_CCMR_CC3S_TI3, + TIM_CCER_CC3E | TIM_CCER_CC3P | TIM_CCER_CC3NP }, + { TIM_CCMR2, TIM_CCMR_CC4S, TIM_CCMR_CC4S_TI4, + TIM_CCER_CC4E | TIM_CCER_CC4P | TIM_CCER_CC4NP }, +}; + +static int stm32_count_capture_configure(struct counter_device *counter, unsigned int ch, + bool enable) +{ + struct stm32_timer_cnt *const priv = counter_priv(counter); + const struct stm32_count_cc_regs *cc; + u32 ccmr, ccer; + + if (ch >= ARRAY_SIZE(stm32_cc) || ch >= priv->nchannels) { + dev_err(counter->parent, "invalid ch: %d\n", ch); + return -EINVAL; + } + + cc = &stm32_cc[ch]; + + /* + * configure channel in input capture mode, map channel 1 on TI1, channel2 on TI2... + * Select both edges / non-inverted to trigger a capture. + */ + if (enable) { + /* first clear possibly latched capture flag upon enabling */ + if (!regmap_test_bits(priv->regmap, TIM_CCER, cc->ccer_bits)) + regmap_write(priv->regmap, TIM_SR, ~TIM_SR_CC_IF(ch)); + regmap_update_bits(priv->regmap, cc->ccmr_reg, cc->ccmr_mask, + cc->ccmr_bits); + regmap_set_bits(priv->regmap, TIM_CCER, cc->ccer_bits); + } else { + regmap_clear_bits(priv->regmap, TIM_CCER, cc->ccer_bits); + regmap_clear_bits(priv->regmap, cc->ccmr_reg, cc->ccmr_mask); + } + + regmap_read(priv->regmap, cc->ccmr_reg, &ccmr); + regmap_read(priv->regmap, TIM_CCER, &ccer); + dev_dbg(counter->parent, "%s(%s) ch%d 0x%08x 0x%08x\n", __func__, enable ? "ena" : "dis", + ch, ccmr, ccer); + + return 0; +} + +static int stm32_count_events_configure(struct counter_device *counter) +{ + struct stm32_timer_cnt *const priv = counter_priv(counter); + struct counter_event_node *event_node; + u32 dier = 0; + int i, ret; + + list_for_each_entry(event_node, &counter->events_list, l) { + switch (event_node->event) { + case COUNTER_EVENT_OVERFLOW_UNDERFLOW: + /* first clear possibly latched UIF before enabling */ + if (!regmap_test_bits(priv->regmap, TIM_DIER, TIM_DIER_UIE)) + regmap_write(priv->regmap, TIM_SR, (u32)~TIM_SR_UIF); + dier |= TIM_DIER_UIE; + break; + case COUNTER_EVENT_CAPTURE: + ret = stm32_count_capture_configure(counter, event_node->channel, true); + if (ret) + return ret; + dier |= TIM_DIER_CCxIE(event_node->channel + 1); + break; + default: + /* should never reach this path */ + return -EINVAL; + } + } + + /* Enable / disable all events at once, from events_list, so write all DIER bits */ + regmap_write(priv->regmap, TIM_DIER, dier); + + /* check for disabled capture events */ + for (i = 0 ; i < priv->nchannels; i++) { + if (!(dier & TIM_DIER_CCxIE(i + 1))) { + ret = stm32_count_capture_configure(counter, i, false); + if (ret) + return ret; + } + } + + return 0; +} + +static int stm32_count_watch_validate(struct counter_device *counter, + const struct counter_watch *watch) +{ + struct stm32_timer_cnt *const priv = counter_priv(counter); + + /* Interrupts are optional */ + if (!priv->nr_irqs) + return -EOPNOTSUPP; + + switch (watch->event) { + case COUNTER_EVENT_CAPTURE: + if (watch->channel >= priv->nchannels) { + dev_err(counter->parent, "Invalid channel %d\n", watch->channel); + return -EINVAL; + } + return 0; + case COUNTER_EVENT_OVERFLOW_UNDERFLOW: return 0; default: return -EINVAL; @@ -274,35 +523,89 @@ static const struct counter_ops stm32_timer_cnt_ops = { .function_read = stm32_count_function_read, .function_write = stm32_count_function_write, .action_read = stm32_action_read, + .events_configure = stm32_count_events_configure, + .watch_validate = stm32_count_watch_validate, +}; + +static int stm32_count_clk_get_freq(struct counter_device *counter, + struct counter_signal *signal, u64 *freq) +{ + struct stm32_timer_cnt *const priv = counter_priv(counter); + + *freq = clk_get_rate(priv->clk); + + return 0; +} + +static struct counter_comp stm32_count_clock_ext[] = { + COUNTER_COMP_FREQUENCY(stm32_count_clk_get_freq), }; static struct counter_signal stm32_signals[] = { + /* + * Need to declare all the signals as a static array, and keep the signals order here, + * even if they're unused or unexisting on some timer instances. It's an abstraction, + * e.g. high level view of the counter features. + * + * Userspace programs may rely on signal0 to be "Channel 1", signal1 to be "Channel 2", + * and so on. When a signal is unexisting, the COUNTER_SYNAPSE_ACTION_NONE can be used, + * to indicate that a signal doesn't affect the counter. + */ { - .id = 0, - .name = "Channel 1 Quadrature A" + .id = STM32_CH1_SIG, + .name = "Channel 1" }, { - .id = 1, - .name = "Channel 1 Quadrature B" - } + .id = STM32_CH2_SIG, + .name = "Channel 2" + }, + { + .id = STM32_CLOCK_SIG, + .name = "Clock", + .ext = stm32_count_clock_ext, + .num_ext = ARRAY_SIZE(stm32_count_clock_ext), + }, + { + .id = STM32_CH3_SIG, + .name = "Channel 3" + }, + { + .id = STM32_CH4_SIG, + .name = "Channel 4" + }, }; static struct counter_synapse stm32_count_synapses[] = { { .actions_list = stm32_synapse_actions, .num_actions = ARRAY_SIZE(stm32_synapse_actions), - .signal = &stm32_signals[0] + .signal = &stm32_signals[STM32_CH1_SIG] }, { .actions_list = stm32_synapse_actions, .num_actions = ARRAY_SIZE(stm32_synapse_actions), - .signal = &stm32_signals[1] - } + .signal = &stm32_signals[STM32_CH2_SIG] + }, + { + .actions_list = stm32_clock_synapse_actions, + .num_actions = ARRAY_SIZE(stm32_clock_synapse_actions), + .signal = &stm32_signals[STM32_CLOCK_SIG] + }, + { + .actions_list = stm32_synapse_actions, + .num_actions = ARRAY_SIZE(stm32_synapse_actions), + .signal = &stm32_signals[STM32_CH3_SIG] + }, + { + .actions_list = stm32_synapse_actions, + .num_actions = ARRAY_SIZE(stm32_synapse_actions), + .signal = &stm32_signals[STM32_CH4_SIG] + }, }; static struct counter_count stm32_counts = { .id = 0, - .name = "Channel 1 Count", + .name = "STM32 Timer Counter", .functions_list = stm32_count_functions, .num_functions = ARRAY_SIZE(stm32_count_functions), .synapses = stm32_count_synapses, @@ -311,13 +614,112 @@ static struct counter_count stm32_counts = { .num_ext = ARRAY_SIZE(stm32_count_ext) }; +static irqreturn_t stm32_timer_cnt_isr(int irq, void *ptr) +{ + struct counter_device *counter = ptr; + struct stm32_timer_cnt *const priv = counter_priv(counter); + u32 clr = GENMASK(31, 0); /* SR flags can be cleared by writing 0 (wr 1 has no effect) */ + u32 sr, dier; + int i; + + regmap_read(priv->regmap, TIM_SR, &sr); + regmap_read(priv->regmap, TIM_DIER, &dier); + /* + * Some status bits in SR don't match with the enable bits in DIER. Only take care of + * the possibly enabled bits in DIER (that matches in between SR and DIER). + */ + dier &= (TIM_DIER_UIE | TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_CC3IE | TIM_DIER_CC4IE); + sr &= dier; + + if (sr & TIM_SR_UIF) { + spin_lock(&priv->lock); + priv->nb_ovf++; + spin_unlock(&priv->lock); + counter_push_event(counter, COUNTER_EVENT_OVERFLOW_UNDERFLOW, 0); + dev_dbg(counter->parent, "COUNTER_EVENT_OVERFLOW_UNDERFLOW\n"); + /* SR flags can be cleared by writing 0, only clear relevant flag */ + clr &= ~TIM_SR_UIF; + } + + /* Check capture events */ + for (i = 0 ; i < priv->nchannels; i++) { + if (sr & TIM_SR_CC_IF(i)) { + counter_push_event(counter, COUNTER_EVENT_CAPTURE, i); + clr &= ~TIM_SR_CC_IF(i); + dev_dbg(counter->parent, "COUNTER_EVENT_CAPTURE, %d\n", i); + } + } + + regmap_write(priv->regmap, TIM_SR, clr); + + return IRQ_HANDLED; +}; + +static void stm32_timer_cnt_detect_channels(struct device *dev, + struct stm32_timer_cnt *priv) +{ + u32 ccer, ccer_backup; + + regmap_read(priv->regmap, TIM_CCER, &ccer_backup); + regmap_set_bits(priv->regmap, TIM_CCER, TIM_CCER_CCXE); + regmap_read(priv->regmap, TIM_CCER, &ccer); + regmap_write(priv->regmap, TIM_CCER, ccer_backup); + priv->nchannels = hweight32(ccer & TIM_CCER_CCXE); + + 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)) + +static const char * const stm32_timer_trigger_compat[] = { + "st,stm32-timer-trigger", + "st,stm32h7-timer-trigger", +}; + +static int stm32_timer_cnt_probe_encoder(struct device *dev, + struct stm32_timer_cnt *priv) +{ + struct device *parent = dev->parent; + struct device_node *tnode = NULL, *pnode = parent->of_node; + int i, ret; + u32 idx; + + /* + * Need to retrieve the trigger node index from DT, to be able + * to determine if the counter supports encoder mode. It also + * enforce backward compatibility, and allow to support other + * counter modes in this driver (when the timer doesn't support + * encoder). + */ + for (i = 0; i < ARRAY_SIZE(stm32_timer_trigger_compat) && !tnode; i++) + tnode = of_get_compatible_child(pnode, stm32_timer_trigger_compat[i]); + if (!tnode) { + dev_err(dev, "Can't find trigger node\n"); + return -ENODATA; + } + + ret = of_property_read_u32(tnode, "reg", &idx); + of_node_put(tnode); + if (ret) { + dev_err(dev, "Can't get index (%d)\n", ret); + return ret; + } + + priv->has_encoder = !!(STM32_TIM_ENCODER_SUPPORTED & BIT(idx)); + + dev_dbg(dev, "encoder support: %s\n", priv->has_encoder ? "yes" : "no"); + + return 0; +} + static int stm32_timer_cnt_probe(struct platform_device *pdev) { struct stm32_timers *ddata = dev_get_drvdata(pdev->dev.parent); struct device *dev = &pdev->dev; struct stm32_timer_cnt *priv; struct counter_device *counter; - int ret; + int i, ret; if (IS_ERR_OR_NULL(ddata)) return -EINVAL; @@ -331,6 +733,13 @@ static int stm32_timer_cnt_probe(struct platform_device *pdev) priv->regmap = ddata->regmap; priv->clk = ddata->clk; priv->max_arr = ddata->max_arr; + priv->nr_irqs = ddata->nr_irqs; + + ret = stm32_timer_cnt_probe_encoder(dev, priv); + if (ret) + return ret; + + stm32_timer_cnt_detect_channels(dev, priv); counter->name = dev_name(dev); counter->parent = dev; @@ -340,8 +749,39 @@ static int stm32_timer_cnt_probe(struct platform_device *pdev) counter->signals = stm32_signals; counter->num_signals = ARRAY_SIZE(stm32_signals); + spin_lock_init(&priv->lock); + platform_set_drvdata(pdev, priv); + /* STM32 Timers can have either 1 global, or 4 dedicated interrupts (optional) */ + if (priv->nr_irqs == 1) { + /* All events reported through the global interrupt */ + ret = devm_request_irq(&pdev->dev, ddata->irq[0], stm32_timer_cnt_isr, + 0, dev_name(dev), counter); + if (ret) { + dev_err(dev, "Failed to request irq %d (err %d)\n", + ddata->irq[0], ret); + return ret; + } + } else { + for (i = 0; i < priv->nr_irqs; i++) { + /* + * Only take care of update IRQ for overflow events, and cc for + * capture events. + */ + if (i != STM32_TIMERS_IRQ_UP && i != STM32_TIMERS_IRQ_CC) + continue; + + ret = devm_request_irq(&pdev->dev, ddata->irq[i], stm32_timer_cnt_isr, + 0, dev_name(dev), counter); + if (ret) { + dev_err(dev, "Failed to request irq %d (err %d)\n", + ddata->irq[i], ret); + return ret; + } + } + } + /* Reset input selector to its default input */ regmap_write(priv->regmap, TIM_TISEL, 0x0); @@ -383,7 +823,11 @@ static int __maybe_unused stm32_timer_cnt_resume(struct device *dev) return ret; if (priv->enabled) { - clk_enable(priv->clk); + ret = clk_enable(priv->clk); + if (ret) { + dev_err(dev, "Cannot enable clock %d\n", ret); + return ret; + } /* Restore registers that may have been lost */ regmap_write(priv->regmap, TIM_SMCR, priv->bak.smcr); @@ -420,4 +864,4 @@ MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>"); MODULE_ALIAS("platform:stm32-timer-counter"); MODULE_DESCRIPTION("STMicroelectronics STM32 TIMER counter driver"); MODULE_LICENSE("GPL v2"); -MODULE_IMPORT_NS(COUNTER); +MODULE_IMPORT_NS("COUNTER"); diff --git a/drivers/counter/ti-ecap-capture.c b/drivers/counter/ti-ecap-capture.c index fb1cb1774674..3faaf7f60539 100644 --- a/drivers/counter/ti-ecap-capture.c +++ b/drivers/counter/ti-ecap-capture.c @@ -369,7 +369,7 @@ static const enum counter_synapse_action ecap_cnt_input_actions[] = { }; static struct counter_comp ecap_cnt_clock_ext[] = { - COUNTER_COMP_SIGNAL_U64("frequency", ecap_cnt_clk_get_freq, NULL), + COUNTER_COMP_FREQUENCY(ecap_cnt_clk_get_freq), }; static const enum counter_signal_polarity ecap_cnt_pol_avail[] = { @@ -537,15 +537,13 @@ static int ecap_cnt_probe(struct platform_device *pdev) return 0; } -static int ecap_cnt_remove(struct platform_device *pdev) +static void ecap_cnt_remove(struct platform_device *pdev) { struct counter_device *counter_dev = platform_get_drvdata(pdev); struct ecap_cnt_dev *ecap_dev = counter_priv(counter_dev); if (ecap_dev->enabled) ecap_cnt_capture_disable(counter_dev); - - return 0; } static int ecap_cnt_suspend(struct device *dev) @@ -576,8 +574,13 @@ static int ecap_cnt_resume(struct device *dev) { struct counter_device *counter_dev = dev_get_drvdata(dev); struct ecap_cnt_dev *ecap_dev = counter_priv(counter_dev); + int ret; - clk_enable(ecap_dev->clk); + ret = clk_enable(ecap_dev->clk); + if (ret) { + dev_err(dev, "Cannot enable clock %d\n", ret); + return ret; + } ecap_cnt_capture_set_evmode(counter_dev, ecap_dev->pm_ctx.ev_mode); @@ -612,4 +615,4 @@ module_platform_driver(ecap_cnt_driver); MODULE_DESCRIPTION("ECAP Capture driver"); MODULE_AUTHOR("Julien Panis <jpanis@baylibre.com>"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(COUNTER); +MODULE_IMPORT_NS("COUNTER"); diff --git a/drivers/counter/ti-eqep.c b/drivers/counter/ti-eqep.c index b0f24cf3e891..bc586eff0dae 100644 --- a/drivers/counter/ti-eqep.c +++ b/drivers/counter/ti-eqep.c @@ -6,7 +6,9 @@ */ #include <linux/bitops.h> +#include <linux/clk.h> #include <linux/counter.h> +#include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/mod_devicetable.h> #include <linux/module.h> @@ -67,6 +69,44 @@ #define QEPCTL_UTE BIT(1) #define QEPCTL_WDE BIT(0) +#define QEINT_UTO BIT(11) +#define QEINT_IEL BIT(10) +#define QEINT_SEL BIT(9) +#define QEINT_PCM BIT(8) +#define QEINT_PCR BIT(7) +#define QEINT_PCO BIT(6) +#define QEINT_PCU BIT(5) +#define QEINT_WTO BIT(4) +#define QEINT_QDC BIT(3) +#define QEINT_PHE BIT(2) +#define QEINT_PCE BIT(1) + +#define QFLG_UTO BIT(11) +#define QFLG_IEL BIT(10) +#define QFLG_SEL BIT(9) +#define QFLG_PCM BIT(8) +#define QFLG_PCR BIT(7) +#define QFLG_PCO BIT(6) +#define QFLG_PCU BIT(5) +#define QFLG_WTO BIT(4) +#define QFLG_QDC BIT(3) +#define QFLG_PHE BIT(2) +#define QFLG_PCE BIT(1) +#define QFLG_INT BIT(0) + +#define QCLR_UTO BIT(11) +#define QCLR_IEL BIT(10) +#define QCLR_SEL BIT(9) +#define QCLR_PCM BIT(8) +#define QCLR_PCR BIT(7) +#define QCLR_PCO BIT(6) +#define QCLR_PCU BIT(5) +#define QCLR_WTO BIT(4) +#define QCLR_QDC BIT(3) +#define QCLR_PHE BIT(2) +#define QCLR_PCE BIT(1) +#define QCLR_INT BIT(0) + /* EQEP Inputs */ enum { TI_EQEP_SIGNAL_QEPA, /* QEPA/XCLK */ @@ -82,20 +122,14 @@ enum ti_eqep_count_func { }; struct ti_eqep_cnt { - struct counter_device counter; struct regmap *regmap32; struct regmap *regmap16; }; -static struct ti_eqep_cnt *ti_eqep_count_from_counter(struct counter_device *counter) -{ - return counter_priv(counter); -} - static int ti_eqep_count_read(struct counter_device *counter, struct counter_count *count, u64 *val) { - struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); + struct ti_eqep_cnt *priv = counter_priv(counter); u32 cnt; regmap_read(priv->regmap32, QPOSCNT, &cnt); @@ -107,7 +141,7 @@ static int ti_eqep_count_read(struct counter_device *counter, static int ti_eqep_count_write(struct counter_device *counter, struct counter_count *count, u64 val) { - struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); + struct ti_eqep_cnt *priv = counter_priv(counter); u32 max; regmap_read(priv->regmap32, QPOSMAX, &max); @@ -121,7 +155,7 @@ static int ti_eqep_function_read(struct counter_device *counter, struct counter_count *count, enum counter_function *function) { - struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); + struct ti_eqep_cnt *priv = counter_priv(counter); u32 qdecctl; regmap_read(priv->regmap16, QDECCTL, &qdecctl); @@ -148,7 +182,7 @@ static int ti_eqep_function_write(struct counter_device *counter, struct counter_count *count, enum counter_function function) { - struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); + struct ti_eqep_cnt *priv = counter_priv(counter); enum ti_eqep_count_func qsrc; switch (function) { @@ -178,7 +212,7 @@ static int ti_eqep_action_read(struct counter_device *counter, struct counter_synapse *synapse, enum counter_synapse_action *action) { - struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); + struct ti_eqep_cnt *priv = counter_priv(counter); enum counter_function function; u32 qdecctl; int err; @@ -238,19 +272,56 @@ static int ti_eqep_action_read(struct counter_device *counter, } } +static int ti_eqep_events_configure(struct counter_device *counter) +{ + struct ti_eqep_cnt *priv = counter_priv(counter); + struct counter_event_node *event_node; + u32 qeint = 0; + + list_for_each_entry(event_node, &counter->events_list, l) { + switch (event_node->event) { + case COUNTER_EVENT_OVERFLOW: + qeint |= QEINT_PCO; + break; + case COUNTER_EVENT_UNDERFLOW: + qeint |= QEINT_PCU; + break; + } + } + + return regmap_write(priv->regmap16, QEINT, qeint); +} + +static int ti_eqep_watch_validate(struct counter_device *counter, + const struct counter_watch *watch) +{ + switch (watch->event) { + case COUNTER_EVENT_OVERFLOW: + case COUNTER_EVENT_UNDERFLOW: + if (watch->channel != 0) + return -EINVAL; + + return 0; + default: + return -EINVAL; + } +} + static const struct counter_ops ti_eqep_counter_ops = { .count_read = ti_eqep_count_read, .count_write = ti_eqep_count_write, .function_read = ti_eqep_function_read, .function_write = ti_eqep_function_write, .action_read = ti_eqep_action_read, + .events_configure = ti_eqep_events_configure, + .watch_validate = ti_eqep_watch_validate, }; static int ti_eqep_position_ceiling_read(struct counter_device *counter, struct counter_count *count, u64 *ceiling) { - struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); + struct ti_eqep_cnt *priv = counter_priv(counter); u32 qposmax; regmap_read(priv->regmap32, QPOSMAX, &qposmax); @@ -264,7 +335,7 @@ static int ti_eqep_position_ceiling_write(struct counter_device *counter, struct counter_count *count, u64 ceiling) { - struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); + struct ti_eqep_cnt *priv = counter_priv(counter); if (ceiling != (u32)ceiling) return -ERANGE; @@ -277,7 +348,7 @@ static int ti_eqep_position_ceiling_write(struct counter_device *counter, static int ti_eqep_position_enable_read(struct counter_device *counter, struct counter_count *count, u8 *enable) { - struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); + struct ti_eqep_cnt *priv = counter_priv(counter); u32 qepctl; regmap_read(priv->regmap16, QEPCTL, &qepctl); @@ -290,7 +361,7 @@ static int ti_eqep_position_enable_read(struct counter_device *counter, static int ti_eqep_position_enable_write(struct counter_device *counter, struct counter_count *count, u8 enable) { - struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); + struct ti_eqep_cnt *priv = counter_priv(counter); regmap_write_bits(priv->regmap16, QEPCTL, QEPCTL_PHEN, enable ? -1 : 0); @@ -354,6 +425,25 @@ static struct counter_count ti_eqep_counts[] = { }, }; +static irqreturn_t ti_eqep_irq_handler(int irq, void *dev_id) +{ + struct counter_device *counter = dev_id; + struct ti_eqep_cnt *priv = counter_priv(counter); + u32 qflg; + + regmap_read(priv->regmap16, QFLG, &qflg); + + if (qflg & QFLG_PCO) + counter_push_event(counter, COUNTER_EVENT_OVERFLOW, 0); + + if (qflg & QFLG_PCU) + counter_push_event(counter, COUNTER_EVENT_UNDERFLOW, 0); + + regmap_write(priv->regmap16, QCLR, qflg); + + return IRQ_HANDLED; +} + static const struct regmap_config ti_eqep_regmap32_config = { .name = "32-bit", .reg_bits = 32, @@ -376,7 +466,8 @@ static int ti_eqep_probe(struct platform_device *pdev) struct counter_device *counter; struct ti_eqep_cnt *priv; void __iomem *base; - int err; + struct clk *clk; + int err, irq; counter = devm_counter_alloc(dev, sizeof(*priv)); if (!counter) @@ -397,6 +488,15 @@ static int ti_eqep_probe(struct platform_device *pdev) if (IS_ERR(priv->regmap16)) return PTR_ERR(priv->regmap16); + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + err = devm_request_threaded_irq(dev, irq, NULL, ti_eqep_irq_handler, + IRQF_ONESHOT, dev_name(dev), counter); + if (err < 0) + return dev_err_probe(dev, err, "failed to request IRQ\n"); + counter->name = dev_name(dev); counter->parent = dev; counter->ops = &ti_eqep_counter_ops; @@ -415,6 +515,10 @@ static int ti_eqep_probe(struct platform_device *pdev) pm_runtime_enable(dev); pm_runtime_get_sync(dev); + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "failed to enable clock\n"); + err = counter_add(counter); if (err < 0) { pm_runtime_put_sync(dev); @@ -425,7 +529,7 @@ static int ti_eqep_probe(struct platform_device *pdev) return 0; } -static int ti_eqep_remove(struct platform_device *pdev) +static void ti_eqep_remove(struct platform_device *pdev) { struct counter_device *counter = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; @@ -433,12 +537,11 @@ static int ti_eqep_remove(struct platform_device *pdev) counter_unregister(counter); pm_runtime_put_sync(dev); pm_runtime_disable(dev); - - return 0; } static const struct of_device_id ti_eqep_of_match[] = { { .compatible = "ti,am3352-eqep", }, + { .compatible = "ti,am62-eqep", }, { }, }; MODULE_DEVICE_TABLE(of, ti_eqep_of_match); @@ -456,4 +559,4 @@ module_platform_driver(ti_eqep_driver); MODULE_AUTHOR("David Lechner <david@lechnology.com>"); MODULE_DESCRIPTION("TI eQEP counter driver"); MODULE_LICENSE("GPL v2"); -MODULE_IMPORT_NS(COUNTER); +MODULE_IMPORT_NS("COUNTER"); |