diff options
Diffstat (limited to 'drivers/iio/trigger')
| -rw-r--r-- | drivers/iio/trigger/Kconfig | 3 | ||||
| -rw-r--r-- | drivers/iio/trigger/iio-trig-hrtimer.c | 51 | ||||
| -rw-r--r-- | drivers/iio/trigger/iio-trig-interrupt.c | 15 | ||||
| -rw-r--r-- | drivers/iio/trigger/iio-trig-loop.c | 9 | ||||
| -rw-r--r-- | drivers/iio/trigger/iio-trig-sysfs.c | 45 | ||||
| -rw-r--r-- | drivers/iio/trigger/stm32-lptimer-trigger.c | 91 | ||||
| -rw-r--r-- | drivers/iio/trigger/stm32-timer-trigger.c | 354 |
7 files changed, 331 insertions, 237 deletions
diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig index a633d2c8e805..7ecb69725b1d 100644 --- a/drivers/iio/trigger/Kconfig +++ b/drivers/iio/trigger/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only # # Industrial I/O standalone triggers # @@ -37,7 +38,7 @@ config IIO_STM32_LPTIMER_TRIGGER config IIO_STM32_TIMER_TRIGGER tristate "STM32 Timer Trigger" - depends on (ARCH_STM32 && OF && MFD_STM32_TIMERS) || COMPILE_TEST + depends on (ARCH_STM32 && MFD_STM32_TIMERS) || COMPILE_TEST help Select this option to enable STM32 Timer Trigger diff --git a/drivers/iio/trigger/iio-trig-hrtimer.c b/drivers/iio/trigger/iio-trig-hrtimer.c index 7accd0187ba1..82c72baccb62 100644 --- a/drivers/iio/trigger/iio-trig-hrtimer.c +++ b/drivers/iio/trigger/iio-trig-hrtimer.c @@ -1,16 +1,12 @@ -/** +// SPDX-License-Identifier: GPL-2.0-only +/* * The industrial I/O periodic hrtimer trigger driver * * Copyright (C) Intuitive Aerial AB * Written by Marten Svanfeldt, marten@intuitiveaerial.com - * Copyright (C) 2012, Analog Device Inc. + * Copyright (C) 2012, Analog Devices Inc. * Author: Lars-Peter Clausen <lars@metafoo.de> * Copyright (C) 2015, Intel Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * */ #include <linux/kernel.h> #include <linux/slab.h> @@ -20,13 +16,16 @@ #include <linux/iio/trigger.h> #include <linux/iio/sw_trigger.h> +/* Defined locally, not in time64.h yet. */ +#define PSEC_PER_SEC 1000000000000LL + /* default sampling frequency - 100Hz */ #define HRTIMER_DEFAULT_SAMPLING_FREQUENCY 100 struct iio_hrtimer_info { struct iio_sw_trigger swt; struct hrtimer timer; - unsigned long sampling_frequency; + int sampling_frequency[2]; ktime_t period; }; @@ -42,7 +41,9 @@ ssize_t iio_hrtimer_show_sampling_frequency(struct device *dev, struct iio_trigger *trig = to_iio_trigger(dev); struct iio_hrtimer_info *info = iio_trigger_get_drvdata(trig); - return snprintf(buf, PAGE_SIZE, "%lu\n", info->sampling_frequency); + return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, + ARRAY_SIZE(info->sampling_frequency), + info->sampling_frequency); } static @@ -52,18 +53,26 @@ ssize_t iio_hrtimer_store_sampling_frequency(struct device *dev, { struct iio_trigger *trig = to_iio_trigger(dev); struct iio_hrtimer_info *info = iio_trigger_get_drvdata(trig); - unsigned long val; - int ret; + unsigned long long val; + u64 period; + int integer, fract, ret; - ret = kstrtoul(buf, 10, &val); + ret = iio_str_to_fixpoint(buf, 100, &integer, &fract); if (ret) return ret; + if (integer < 0 || fract < 0) + return -ERANGE; + + val = fract + 1000ULL * integer; /* mHz */ - if (!val || val > NSEC_PER_SEC) + if (!val || val > UINT_MAX) return -EINVAL; - info->sampling_frequency = val; - info->period = NSEC_PER_SEC / val; + info->sampling_frequency[0] = integer; /* Hz */ + info->sampling_frequency[1] = fract * 1000; /* uHz */ + period = PSEC_PER_SEC; + do_div(period, val); + info->period = period; /* nS */ return len; } @@ -106,7 +115,7 @@ static int iio_trig_hrtimer_set_state(struct iio_trigger *trig, bool state) if (state) hrtimer_start(&trig_info->timer, trig_info->period, - HRTIMER_MODE_REL); + HRTIMER_MODE_REL_HARD); else hrtimer_cancel(&trig_info->timer); @@ -126,7 +135,7 @@ static struct iio_sw_trigger *iio_trig_hrtimer_probe(const char *name) if (!trig_info) return ERR_PTR(-ENOMEM); - trig_info->swt.trigger = iio_trigger_alloc("%s", name); + trig_info->swt.trigger = iio_trigger_alloc(NULL, "%s", name); if (!trig_info->swt.trigger) { ret = -ENOMEM; goto err_free_trig_info; @@ -136,11 +145,11 @@ static struct iio_sw_trigger *iio_trig_hrtimer_probe(const char *name) trig_info->swt.trigger->ops = &iio_hrtimer_trigger_ops; trig_info->swt.trigger->dev.groups = iio_hrtimer_attr_groups; - hrtimer_init(&trig_info->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - trig_info->timer.function = iio_hrtimer_trig_handler; + hrtimer_setup(&trig_info->timer, iio_hrtimer_trig_handler, CLOCK_MONOTONIC, + HRTIMER_MODE_REL_HARD); - trig_info->sampling_frequency = HRTIMER_DEFAULT_SAMPLING_FREQUENCY; - trig_info->period = NSEC_PER_SEC / trig_info->sampling_frequency; + trig_info->sampling_frequency[0] = HRTIMER_DEFAULT_SAMPLING_FREQUENCY; + trig_info->period = NSEC_PER_SEC / trig_info->sampling_frequency[0]; ret = iio_trigger_register(trig_info->swt.trigger); if (ret) diff --git a/drivers/iio/trigger/iio-trig-interrupt.c b/drivers/iio/trigger/iio-trig-interrupt.c index 171c4ed03543..21c6b6292a72 100644 --- a/drivers/iio/trigger/iio-trig-interrupt.c +++ b/drivers/iio/trigger/iio-trig-interrupt.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Industrial I/O - generic interrupt based trigger support * * Copyright (c) 2008-2013 Jonathan Cameron - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. */ #include <linux/kernel.h> @@ -28,9 +25,6 @@ static irqreturn_t iio_interrupt_trigger_poll(int irq, void *private) return IRQ_HANDLED; } -static const struct iio_trigger_ops iio_interrupt_trigger_ops = { -}; - static int iio_interrupt_trigger_probe(struct platform_device *pdev) { struct iio_interrupt_trigger_info *trig_info; @@ -48,7 +42,7 @@ static int iio_interrupt_trigger_probe(struct platform_device *pdev) irq = irq_res->start; - trig = iio_trigger_alloc("irqtrig%d", irq); + trig = iio_trigger_alloc(NULL, "irqtrig%d", irq); if (!trig) { ret = -ENOMEM; goto error_ret; @@ -61,7 +55,6 @@ static int iio_interrupt_trigger_probe(struct platform_device *pdev) } iio_trigger_set_drvdata(trig, trig_info); trig_info->irq = irq; - trig->ops = &iio_interrupt_trigger_ops; ret = request_irq(irq, iio_interrupt_trigger_poll, irqflags, trig->name, trig); if (ret) { @@ -88,7 +81,7 @@ error_ret: return ret; } -static int iio_interrupt_trigger_remove(struct platform_device *pdev) +static void iio_interrupt_trigger_remove(struct platform_device *pdev) { struct iio_trigger *trig; struct iio_interrupt_trigger_info *trig_info; @@ -99,8 +92,6 @@ static int iio_interrupt_trigger_remove(struct platform_device *pdev) free_irq(trig_info->irq, trig); kfree(trig_info); iio_trigger_free(trig); - - return 0; } static struct platform_driver iio_interrupt_trigger_driver = { diff --git a/drivers/iio/trigger/iio-trig-loop.c b/drivers/iio/trigger/iio-trig-loop.c index 94a90e0a3fdb..7aaed0611899 100644 --- a/drivers/iio/trigger/iio-trig-loop.c +++ b/drivers/iio/trigger/iio-trig-loop.c @@ -1,8 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2016 Jonathan Cameron <jic23@kernel.org> * - * Licensed under the GPL-2. - * * Based on a mashup of the hrtimer trigger and continuous sampling proposal of * Gregor Boirie <gregor.boirie@parrot.com> * @@ -47,7 +46,7 @@ static int iio_loop_thread(void *data) set_freezable(); do { - iio_trigger_poll_chained(trig); + iio_trigger_poll_nested(trig); } while (likely(!kthread_freezable_should_stop(NULL))); return 0; @@ -60,7 +59,7 @@ static int iio_loop_trigger_set_state(struct iio_trigger *trig, bool state) if (state) { loop_trig->task = kthread_run(iio_loop_thread, trig, trig->name); - if (unlikely(IS_ERR(loop_trig->task))) { + if (IS_ERR(loop_trig->task)) { dev_err(&trig->dev, "failed to create trigger loop thread\n"); return PTR_ERR(loop_trig->task); @@ -85,7 +84,7 @@ static struct iio_sw_trigger *iio_trig_loop_probe(const char *name) if (!trig_info) return ERR_PTR(-ENOMEM); - trig_info->swt.trigger = iio_trigger_alloc("%s", name); + trig_info->swt.trigger = iio_trigger_alloc(NULL, "%s", name); if (!trig_info->swt.trigger) { ret = -ENOMEM; goto err_free_trig_info; diff --git a/drivers/iio/trigger/iio-trig-sysfs.c b/drivers/iio/trigger/iio-trig-sysfs.c index 45c4897295d6..575d725696a9 100644 --- a/drivers/iio/trigger/iio-trig-sysfs.c +++ b/drivers/iio/trigger/iio-trig-sysfs.c @@ -1,8 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - * */ #include <linux/kernel.h> @@ -126,9 +124,6 @@ static const struct attribute_group *iio_sysfs_trigger_attr_groups[] = { NULL }; -static const struct iio_trigger_ops iio_sysfs_trigger_ops = { -}; - static int iio_sysfs_trigger_probe(int id) { struct iio_sysfs_trig *t; @@ -143,61 +138,59 @@ static int iio_sysfs_trigger_probe(int id) } if (foundit) { ret = -EINVAL; - goto out1; + goto err_unlock; } t = kmalloc(sizeof(*t), GFP_KERNEL); if (t == NULL) { ret = -ENOMEM; - goto out1; + goto err_unlock; } t->id = id; - t->trig = iio_trigger_alloc("sysfstrig%d", id); + t->trig = iio_trigger_alloc(&iio_sysfs_trig_dev, "sysfstrig%d", id); if (!t->trig) { ret = -ENOMEM; - goto free_t; + goto err_free_sys_trig; } t->trig->dev.groups = iio_sysfs_trigger_attr_groups; - t->trig->ops = &iio_sysfs_trigger_ops; - t->trig->dev.parent = &iio_sysfs_trig_dev; iio_trigger_set_drvdata(t->trig, t); - init_irq_work(&t->work, iio_sysfs_trigger_work); + t->work = IRQ_WORK_INIT_HARD(iio_sysfs_trigger_work); ret = iio_trigger_register(t->trig); if (ret) - goto out2; + goto err_free_trig; list_add(&t->l, &iio_sysfs_trig_list); __module_get(THIS_MODULE); mutex_unlock(&iio_sysfs_trig_list_mut); return 0; -out2: +err_free_trig: iio_trigger_free(t->trig); -free_t: +err_free_sys_trig: kfree(t); -out1: +err_unlock: mutex_unlock(&iio_sysfs_trig_list_mut); return ret; } static int iio_sysfs_trigger_remove(int id) { - bool foundit = false; - struct iio_sysfs_trig *t; + struct iio_sysfs_trig *t = NULL, *iter; mutex_lock(&iio_sysfs_trig_list_mut); - list_for_each_entry(t, &iio_sysfs_trig_list, l) - if (id == t->id) { - foundit = true; + list_for_each_entry(iter, &iio_sysfs_trig_list, l) + if (id == iter->id) { + t = iter; break; } - if (!foundit) { + if (!t) { mutex_unlock(&iio_sysfs_trig_list_mut); return -EINVAL; } iio_trigger_unregister(t->trig); + irq_work_sync(&t->work); iio_trigger_free(t->trig); list_del(&t->l); @@ -210,9 +203,13 @@ static int iio_sysfs_trigger_remove(int id) static int __init iio_sysfs_trig_init(void) { + int ret; device_initialize(&iio_sysfs_trig_dev); dev_set_name(&iio_sysfs_trig_dev, "iio_sysfs_trigger"); - return device_add(&iio_sysfs_trig_dev); + ret = device_add(&iio_sysfs_trig_dev); + if (ret) + put_device(&iio_sysfs_trig_dev); + return ret; } module_init(iio_sysfs_trig_init); diff --git a/drivers/iio/trigger/stm32-lptimer-trigger.c b/drivers/iio/trigger/stm32-lptimer-trigger.c index 98cdc7e47f3d..c7bab18221c7 100644 --- a/drivers/iio/trigger/stm32-lptimer-trigger.c +++ b/drivers/iio/trigger/stm32-lptimer-trigger.c @@ -9,21 +9,51 @@ * Inspired by Benjamin Gaignard's stm32-timer-trigger driver */ +#include <linux/export.h> #include <linux/iio/timer/stm32-lptim-trigger.h> #include <linux/mfd/stm32-lptimer.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/property.h> -/* List Low-Power Timer triggers */ -static const char * const stm32_lptim_triggers[] = { - LPTIM1_OUT, - LPTIM2_OUT, - LPTIM3_OUT, +/* Maximum triggers + one trailing null entry to indicate the end of array */ +#define MAX_TRIGGERS 3 + +struct stm32_lptim_cfg { + const char * const (*triggers)[MAX_TRIGGERS]; + unsigned int nb_triggers; +}; + +/* List Low-Power Timer triggers for H7, MP13, MP15 */ +static const char * const stm32_lptim_triggers[][MAX_TRIGGERS] = { + { LPTIM1_OUT,}, + { LPTIM2_OUT,}, + { LPTIM3_OUT,}, +}; + +/* List Low-Power Timer triggers for STM32MP25 */ +static const char * const stm32mp25_lptim_triggers[][MAX_TRIGGERS] = { + { LPTIM1_CH1, LPTIM1_CH2, }, + { LPTIM2_CH1, LPTIM2_CH2, }, + { LPTIM3_CH1,}, + { LPTIM4_CH1,}, + { LPTIM5_OUT,}, +}; + +static const struct stm32_lptim_cfg stm32mp15_lptim_cfg = { + .triggers = stm32_lptim_triggers, + .nb_triggers = ARRAY_SIZE(stm32_lptim_triggers), +}; + +static const struct stm32_lptim_cfg stm32mp25_lptim_cfg = { + .triggers = stm32mp25_lptim_triggers, + .nb_triggers = ARRAY_SIZE(stm32mp25_lptim_triggers), }; struct stm32_lptim_trigger { struct device *dev; - const char *trg; + const char * const *triggers; }; static int stm32_lptim_validate_device(struct iio_trigger *trig, @@ -54,50 +84,57 @@ EXPORT_SYMBOL(is_stm32_lptim_trigger); static int stm32_lptim_setup_trig(struct stm32_lptim_trigger *priv) { - struct iio_trigger *trig; + const char * const *cur = priv->triggers; + int ret; - trig = devm_iio_trigger_alloc(priv->dev, "%s", priv->trg); - if (!trig) - return -ENOMEM; + while (cur && *cur) { + struct iio_trigger *trig; + + trig = devm_iio_trigger_alloc(priv->dev, "%s", *cur); + if (!trig) + return -ENOMEM; + + trig->dev.parent = priv->dev->parent; + trig->ops = &stm32_lptim_trigger_ops; + iio_trigger_set_drvdata(trig, priv); - trig->dev.parent = priv->dev->parent; - trig->ops = &stm32_lptim_trigger_ops; - iio_trigger_set_drvdata(trig, priv); + ret = devm_iio_trigger_register(priv->dev, trig); + if (ret) + return ret; + cur++; + } - return devm_iio_trigger_register(priv->dev, trig); + return 0; } static int stm32_lptim_trigger_probe(struct platform_device *pdev) { struct stm32_lptim_trigger *priv; + struct stm32_lptim_cfg const *lptim_cfg; u32 index; - int ret; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - if (of_property_read_u32(pdev->dev.of_node, "reg", &index)) + if (device_property_read_u32(&pdev->dev, "reg", &index)) return -EINVAL; - if (index >= ARRAY_SIZE(stm32_lptim_triggers)) + lptim_cfg = device_get_match_data(&pdev->dev); + + if (index >= lptim_cfg->nb_triggers) return -EINVAL; priv->dev = &pdev->dev; - priv->trg = stm32_lptim_triggers[index]; - - ret = stm32_lptim_setup_trig(priv); - if (ret) - return ret; + priv->triggers = lptim_cfg->triggers[index]; - platform_set_drvdata(pdev, priv); - - return 0; + return stm32_lptim_setup_trig(priv); } static const struct of_device_id stm32_lptim_trig_of_match[] = { - { .compatible = "st,stm32-lptimer-trigger", }, - {}, + { .compatible = "st,stm32-lptimer-trigger", .data = &stm32mp15_lptim_cfg }, + { .compatible = "st,stm32mp25-lptimer-trigger", .data = &stm32mp25_lptim_cfg}, + { } }; MODULE_DEVICE_TABLE(of, stm32_lptim_trig_of_match); diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c index ccf1ce653b25..3b9a3a6cbb25 100644 --- a/drivers/iio/trigger/stm32-timer-trigger.c +++ b/drivers/iio/trigger/stm32-timer-trigger.c @@ -6,14 +6,16 @@ * */ +#include <linux/export.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> #include <linux/iio/timer/stm32-timer-trigger.h> #include <linux/iio/trigger.h> #include <linux/mfd/stm32-timers.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/of_device.h> +#include <linux/property.h> #define MAX_TRIGGERS 7 #define MAX_VALIDS 5 @@ -37,6 +39,9 @@ static const void *triggers_table[][MAX_TRIGGERS] = { { TIM15_TRGO,}, { TIM16_OC1,}, { TIM17_OC1,}, + { }, /* timer 18 */ + { }, /* timer 19 */ + { TIM20_TRGO, TIM20_TRGO2, TIM20_OC1, TIM20_OC2, TIM20_OC3, }, }; /* List the triggers accepted by each timer */ @@ -75,14 +80,27 @@ static const void *stm32h7_valids_table[][MAX_VALIDS] = { { }, /* timer 17 */ }; +struct stm32_timer_trigger_regs { + u32 cr1; + u32 cr2; + u32 psc; + u32 arr; + u32 cnt; + u32 smcr; +}; + struct stm32_timer_trigger { struct device *dev; struct regmap *regmap; struct clk *clk; + bool enabled; u32 max_arr; const void *triggers; const void *valids; bool has_trgo2; + struct mutex lock; /* concurrent sysfs configuration */ + struct list_head tr_list; + struct stm32_timer_trigger_regs bak; }; struct stm32_timer_trigger_cfg { @@ -105,8 +123,8 @@ static int stm32_timer_start(struct stm32_timer_trigger *priv, unsigned int frequency) { unsigned long long prd, div; - int prescaler = 0; - u32 ccer, cr1; + int prescaler = 0, ret; + u32 ccer; /* Period and prescaler values depends of clock rate */ div = (unsigned long long)clk_get_rate(priv->clk); @@ -136,13 +154,17 @@ static int stm32_timer_start(struct stm32_timer_trigger *priv, if (ccer & TIM_CCER_CCXE) return -EBUSY; - regmap_read(priv->regmap, TIM_CR1, &cr1); - if (!(cr1 & TIM_CR1_CEN)) - clk_enable(priv->clk); + guard(mutex)(&priv->lock); + if (!priv->enabled) { + priv->enabled = true; + ret = clk_enable(priv->clk); + if (ret) + return ret; + } regmap_write(priv->regmap, TIM_PSC, prescaler); regmap_write(priv->regmap, TIM_ARR, prd - 1); - regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, TIM_CR1_ARPE); + regmap_set_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE); /* Force master mode to update mode */ if (stm32_timer_is_trgo2_name(trig->name)) @@ -153,34 +175,44 @@ static int stm32_timer_start(struct stm32_timer_trigger *priv, 0x2 << TIM_CR2_MMS_SHIFT); /* Make sure that registers are updated */ - regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG); + regmap_set_bits(priv->regmap, TIM_EGR, TIM_EGR_UG); /* Enable controller */ - regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN); + regmap_set_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN); return 0; } -static void stm32_timer_stop(struct stm32_timer_trigger *priv) +static void stm32_timer_stop(struct stm32_timer_trigger *priv, + struct iio_trigger *trig) { - u32 ccer, cr1; + u32 ccer; regmap_read(priv->regmap, TIM_CCER, &ccer); if (ccer & TIM_CCER_CCXE) return; - regmap_read(priv->regmap, TIM_CR1, &cr1); - if (cr1 & TIM_CR1_CEN) - clk_disable(priv->clk); - + mutex_lock(&priv->lock); /* Stop timer */ - regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, 0); - regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0); + regmap_clear_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE); + regmap_clear_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN); regmap_write(priv->regmap, TIM_PSC, 0); regmap_write(priv->regmap, TIM_ARR, 0); + /* Force disable master mode */ + if (stm32_timer_is_trgo2_name(trig->name)) + regmap_clear_bits(priv->regmap, TIM_CR2, TIM_CR2_MMS2); + else + regmap_clear_bits(priv->regmap, TIM_CR2, TIM_CR2_MMS); + /* Make sure that registers are updated */ - regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG); + regmap_set_bits(priv->regmap, TIM_EGR, TIM_EGR_UG); + + if (priv->enabled) { + priv->enabled = false; + clk_disable(priv->clk); + } + mutex_unlock(&priv->lock); } static ssize_t stm32_tt_store_frequency(struct device *dev, @@ -197,7 +229,7 @@ static ssize_t stm32_tt_store_frequency(struct device *dev, return ret; if (freq == 0) { - stm32_timer_stop(priv); + stm32_timer_stop(priv, trig); } else { ret = stm32_timer_start(priv, trig, freq); if (ret) @@ -270,7 +302,7 @@ static ssize_t stm32_tt_show_master_mode(struct device *dev, else cr2 = (cr2 & TIM_CR2_MMS) >> TIM_CR2_MMS_SHIFT; - return snprintf(buf, PAGE_SIZE, "%s\n", master_mode_table[cr2]); + return sysfs_emit(buf, "%s\n", master_mode_table[cr2]); } static ssize_t stm32_tt_store_master_mode(struct device *dev, @@ -280,7 +312,7 @@ static ssize_t stm32_tt_store_master_mode(struct device *dev, struct stm32_timer_trigger *priv = dev_get_drvdata(dev); struct iio_trigger *trig = to_iio_trigger(dev); u32 mask, shift, master_mode_max; - int i; + int i, ret; if (stm32_timer_is_trgo2_name(trig->name)) { mask = TIM_CR2_MMS2; @@ -295,11 +327,16 @@ static ssize_t stm32_tt_store_master_mode(struct device *dev, for (i = 0; i <= master_mode_max; i++) { if (!strncmp(master_mode_table[i], buf, strlen(master_mode_table[i]))) { + guard(mutex)(&priv->lock); + if (!priv->enabled) { + /* Clock should be enabled first */ + priv->enabled = true; + ret = clk_enable(priv->clk); + if (ret) + return ret; + } regmap_update_bits(priv->regmap, TIM_CR2, mask, i << shift); - /* Make sure that registers are updated */ - regmap_update_bits(priv->regmap, TIM_EGR, - TIM_EGR_UG, TIM_EGR_UG); return len; } } @@ -357,11 +394,21 @@ static const struct attribute_group *stm32_trigger_attr_groups[] = { static const struct iio_trigger_ops timer_trigger_ops = { }; -static int stm32_setup_iio_triggers(struct stm32_timer_trigger *priv) +static void stm32_unregister_iio_triggers(struct stm32_timer_trigger *priv) +{ + struct iio_trigger *tr; + + list_for_each_entry(tr, &priv->tr_list, alloc_list) + iio_trigger_unregister(tr); +} + +static int stm32_register_iio_triggers(struct stm32_timer_trigger *priv) { int ret; const char * const *cur = priv->triggers; + INIT_LIST_HEAD(&priv->tr_list); + while (cur && *cur) { struct iio_trigger *trig; bool cur_is_trgo = stm32_timer_is_trgo_name(*cur); @@ -388,9 +435,13 @@ static int stm32_setup_iio_triggers(struct stm32_timer_trigger *priv) iio_trigger_set_drvdata(trig, priv); - ret = devm_iio_trigger_register(priv->dev, trig); - if (ret) + ret = iio_trigger_register(trig); + if (ret) { + stm32_unregister_iio_triggers(priv); return ret; + } + + list_add_tail(&trig->alloc_list, &priv->tr_list); cur++; } @@ -437,7 +488,7 @@ static int stm32_counter_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct stm32_timer_trigger *priv = iio_priv(indio_dev); - u32 dat; + int ret; switch (mask) { case IIO_CHAN_INFO_RAW: @@ -447,24 +498,29 @@ static int stm32_counter_write_raw(struct iio_dev *indio_dev, /* fixed scale */ return -EINVAL; - case IIO_CHAN_INFO_ENABLE: + case IIO_CHAN_INFO_ENABLE: { + guard(mutex)(&priv->lock); if (val) { - regmap_read(priv->regmap, TIM_CR1, &dat); - if (!(dat & TIM_CR1_CEN)) - clk_enable(priv->clk); - regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, - TIM_CR1_CEN); + if (!priv->enabled) { + priv->enabled = true; + ret = clk_enable(priv->clk); + if (ret) + return ret; + } + regmap_set_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN); } else { - regmap_read(priv->regmap, TIM_CR1, &dat); - regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, - 0); - if (dat & TIM_CR1_CEN) + regmap_clear_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN); + if (priv->enabled) { + priv->enabled = false; clk_disable(priv->clk); + } } + return 0; } - - return -EINVAL; + default: + return -EINVAL; + } } static int stm32_counter_validate_trigger(struct iio_dev *indio_dev, @@ -507,7 +563,7 @@ static int stm32_set_trigger_mode(struct iio_dev *indio_dev, { struct stm32_timer_trigger *priv = iio_priv(indio_dev); - regmap_update_bits(priv->regmap, TIM_SMCR, TIM_SMCR_SMS, TIM_SMCR_SMS); + regmap_set_bits(priv->regmap, TIM_SMCR, TIM_SMCR_SMS); return 0; } @@ -556,7 +612,7 @@ static int stm32_set_enable_mode(struct iio_dev *indio_dev, { struct stm32_timer_trigger *priv = iio_priv(indio_dev); int sms = stm32_enable_mode2sms(mode); - u32 val; + int ret; if (sms < 0) return sms; @@ -564,10 +620,14 @@ static int stm32_set_enable_mode(struct iio_dev *indio_dev, * Triggered mode sets CEN bit automatically by hardware. So, first * enable counter clock, so it can use it. Keeps it in sync with CEN. */ - if (sms == 6) { - regmap_read(priv->regmap, TIM_CR1, &val); - if (!(val & TIM_CR1_CEN)) - clk_enable(priv->clk); + scoped_guard(mutex, &priv->lock) { + if (sms == 6 && !priv->enabled) { + ret = clk_enable(priv->clk); + if (ret) + return ret; + + priv->enabled = true; + } } regmap_update_bits(priv->regmap, TIM_SMCR, TIM_SMCR_SMS, sms); @@ -608,86 +668,6 @@ static const struct iio_enum stm32_enable_mode_enum = { .get = stm32_get_enable_mode }; -static const char *const stm32_quadrature_modes[] = { - "channel_A", - "channel_B", - "quadrature", -}; - -static int stm32_set_quadrature_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - unsigned int mode) -{ - struct stm32_timer_trigger *priv = iio_priv(indio_dev); - - regmap_update_bits(priv->regmap, TIM_SMCR, TIM_SMCR_SMS, mode + 1); - - return 0; -} - -static int stm32_get_quadrature_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - struct stm32_timer_trigger *priv = iio_priv(indio_dev); - u32 smcr; - int mode; - - regmap_read(priv->regmap, TIM_SMCR, &smcr); - mode = (smcr & TIM_SMCR_SMS) - 1; - if ((mode < 0) || (mode > ARRAY_SIZE(stm32_quadrature_modes))) - return -EINVAL; - - return mode; -} - -static const struct iio_enum stm32_quadrature_mode_enum = { - .items = stm32_quadrature_modes, - .num_items = ARRAY_SIZE(stm32_quadrature_modes), - .set = stm32_set_quadrature_mode, - .get = stm32_get_quadrature_mode -}; - -static const char *const stm32_count_direction_states[] = { - "up", - "down" -}; - -static int stm32_set_count_direction(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - unsigned int dir) -{ - struct stm32_timer_trigger *priv = iio_priv(indio_dev); - u32 val; - int mode; - - /* In encoder mode, direction is RO (given by TI1/TI2 signals) */ - regmap_read(priv->regmap, TIM_SMCR, &val); - mode = (val & TIM_SMCR_SMS) - 1; - if ((mode >= 0) || (mode < ARRAY_SIZE(stm32_quadrature_modes))) - return -EBUSY; - - return regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_DIR, - dir ? TIM_CR1_DIR : 0); -} - -static int stm32_get_count_direction(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - struct stm32_timer_trigger *priv = iio_priv(indio_dev); - u32 cr1; - - regmap_read(priv->regmap, TIM_CR1, &cr1); - - return ((cr1 & TIM_CR1_DIR) ? 1 : 0); -} - -static const struct iio_enum stm32_count_direction_enum = { - .items = stm32_count_direction_states, - .num_items = ARRAY_SIZE(stm32_count_direction_states), - .set = stm32_set_count_direction, - .get = stm32_get_count_direction -}; - static ssize_t stm32_count_get_preset(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, @@ -715,7 +695,7 @@ static ssize_t stm32_count_set_preset(struct iio_dev *indio_dev, return ret; /* TIMx_ARR register shouldn't be buffered (ARPE=0) */ - regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, 0); + regmap_clear_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE); regmap_write(priv->regmap, TIM_ARR, preset); return len; @@ -728,15 +708,11 @@ static const struct iio_chan_spec_ext_info stm32_trigger_count_info[] = { .read = stm32_count_get_preset, .write = stm32_count_set_preset }, - IIO_ENUM("count_direction", IIO_SEPARATE, &stm32_count_direction_enum), - IIO_ENUM_AVAILABLE("count_direction", &stm32_count_direction_enum), - IIO_ENUM("quadrature_mode", IIO_SEPARATE, &stm32_quadrature_mode_enum), - IIO_ENUM_AVAILABLE("quadrature_mode", &stm32_quadrature_mode_enum), IIO_ENUM("enable_mode", IIO_SEPARATE, &stm32_enable_mode_enum), - IIO_ENUM_AVAILABLE("enable_mode", &stm32_enable_mode_enum), + IIO_ENUM_AVAILABLE("enable_mode", IIO_SHARED_BY_TYPE, &stm32_enable_mode_enum), IIO_ENUM("trigger_mode", IIO_SEPARATE, &stm32_trigger_mode_enum), - IIO_ENUM_AVAILABLE("trigger_mode", &stm32_trigger_mode_enum), - {} + IIO_ENUM_AVAILABLE("trigger_mode", IIO_SHARED_BY_TYPE, &stm32_trigger_mode_enum), + { } }; static const struct iio_chan_spec stm32_trigger_channel = { @@ -760,12 +736,10 @@ static struct stm32_timer_trigger *stm32_setup_counter_device(struct device *dev return NULL; indio_dev->name = dev_name(dev); - indio_dev->dev.parent = dev; indio_dev->info = &stm32_trigger_info; indio_dev->modes = INDIO_HARDWARE_TRIGGERED; indio_dev->num_channels = 1; indio_dev->channels = &stm32_trigger_channel; - indio_dev->dev.of_node = dev->of_node; ret = devm_iio_device_register(dev, indio_dev); if (ret) @@ -795,9 +769,9 @@ static void stm32_timer_detect_trgo2(struct stm32_timer_trigger *priv) * Master mode selection 2 bits can only be written and read back when * timer supports it. */ - regmap_update_bits(priv->regmap, TIM_CR2, TIM_CR2_MMS2, TIM_CR2_MMS2); + regmap_set_bits(priv->regmap, TIM_CR2, TIM_CR2_MMS2); regmap_read(priv->regmap, TIM_CR2, &val); - regmap_update_bits(priv->regmap, TIM_CR2, TIM_CR2_MMS2, 0); + regmap_clear_bits(priv->regmap, TIM_CR2, TIM_CR2_MMS2); priv->has_trgo2 = !!val; } @@ -810,18 +784,18 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev) unsigned int index; int ret; - if (of_property_read_u32(dev->of_node, "reg", &index)) - return -EINVAL; + ret = device_property_read_u32(dev, "reg", &index); + if (ret) + return ret; - cfg = (const struct stm32_timer_trigger_cfg *) - of_match_device(dev->driver->of_match_table, dev)->data; + cfg = device_get_match_data(dev); if (index >= ARRAY_SIZE(triggers_table) || index >= cfg->num_valids_table) return -EINVAL; /* Create an IIO device only if we have triggers to be validated */ - if (*cfg->valids_table[index]) + if (cfg->valids_table && *cfg->valids_table[index]) priv = stm32_setup_counter_device(dev); else priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -834,10 +808,12 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev) priv->clk = ddata->clk; priv->max_arr = ddata->max_arr; priv->triggers = triggers_table[index]; - priv->valids = cfg->valids_table[index]; + if (cfg->valids_table && *cfg->valids_table[index]) + priv->valids = cfg->valids_table[index]; stm32_timer_detect_trgo2(priv); + mutex_init(&priv->lock); - ret = stm32_setup_iio_triggers(priv); + ret = stm32_register_iio_triggers(priv); if (ret) return ret; @@ -846,6 +822,75 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev) return 0; } +static void stm32_timer_trigger_remove(struct platform_device *pdev) +{ + struct stm32_timer_trigger *priv = platform_get_drvdata(pdev); + u32 val; + + /* Unregister triggers before everything can be safely turned off */ + stm32_unregister_iio_triggers(priv); + + /* Check if nobody else use the timer, then disable it */ + regmap_read(priv->regmap, TIM_CCER, &val); + if (!(val & TIM_CCER_CCXE)) + regmap_clear_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN); + + if (priv->enabled) + clk_disable(priv->clk); +} + +static int stm32_timer_trigger_suspend(struct device *dev) +{ + struct stm32_timer_trigger *priv = dev_get_drvdata(dev); + + /* Only take care of enabled timer: don't disturb other MFD child */ + if (priv->enabled) { + /* Backup registers that may get lost in low power mode */ + regmap_read(priv->regmap, TIM_CR1, &priv->bak.cr1); + regmap_read(priv->regmap, TIM_CR2, &priv->bak.cr2); + regmap_read(priv->regmap, TIM_PSC, &priv->bak.psc); + regmap_read(priv->regmap, TIM_ARR, &priv->bak.arr); + regmap_read(priv->regmap, TIM_CNT, &priv->bak.cnt); + regmap_read(priv->regmap, TIM_SMCR, &priv->bak.smcr); + + /* Disable the timer */ + regmap_clear_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN); + clk_disable(priv->clk); + } + + return 0; +} + +static int stm32_timer_trigger_resume(struct device *dev) +{ + struct stm32_timer_trigger *priv = dev_get_drvdata(dev); + int ret; + + if (priv->enabled) { + ret = clk_enable(priv->clk); + if (ret) + return ret; + + /* restore master/slave modes */ + regmap_write(priv->regmap, TIM_SMCR, priv->bak.smcr); + regmap_write(priv->regmap, TIM_CR2, priv->bak.cr2); + + /* restore sampling_frequency (trgo / trgo2 triggers) */ + regmap_write(priv->regmap, TIM_PSC, priv->bak.psc); + regmap_write(priv->regmap, TIM_ARR, priv->bak.arr); + regmap_write(priv->regmap, TIM_CNT, priv->bak.cnt); + + /* Also re-enables the timer */ + regmap_write(priv->regmap, TIM_CR1, priv->bak.cr1); + } + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(stm32_timer_trigger_pm_ops, + stm32_timer_trigger_suspend, + stm32_timer_trigger_resume); + static const struct stm32_timer_trigger_cfg stm32_timer_trg_cfg = { .valids_table = valids_table, .num_valids_table = ARRAY_SIZE(valids_table), @@ -856,6 +901,16 @@ static const struct stm32_timer_trigger_cfg stm32h7_timer_trg_cfg = { .num_valids_table = ARRAY_SIZE(stm32h7_valids_table), }; +static const struct stm32_timer_trigger_cfg stm32mp25_timer_trg_cfg = { + /* + * valids_table not used: counter framework is now superseding the deprecated IIO + * counter interface (IIO_COUNT), so don't support it. num_valids_table is only + * kept here to register the IIO HW triggers. valids_table should be moved at some + * point to the stm32-timer-cnt driver instead. + */ + .num_valids_table = ARRAY_SIZE(triggers_table), +}; + static const struct of_device_id stm32_trig_of_match[] = { { .compatible = "st,stm32-timer-trigger", @@ -863,20 +918,25 @@ static const struct of_device_id stm32_trig_of_match[] = { }, { .compatible = "st,stm32h7-timer-trigger", .data = (void *)&stm32h7_timer_trg_cfg, + }, { + .compatible = "st,stm32mp25-timer-trigger", + .data = (void *)&stm32mp25_timer_trg_cfg, }, - { /* end node */ }, + { } }; MODULE_DEVICE_TABLE(of, stm32_trig_of_match); static struct platform_driver stm32_timer_trigger_driver = { .probe = stm32_timer_trigger_probe, + .remove = stm32_timer_trigger_remove, .driver = { .name = "stm32-timer-trigger", .of_match_table = stm32_trig_of_match, + .pm = pm_sleep_ptr(&stm32_timer_trigger_pm_ops), }, }; module_platform_driver(stm32_timer_trigger_driver); -MODULE_ALIAS("platform: stm32-timer-trigger"); +MODULE_ALIAS("platform:stm32-timer-trigger"); MODULE_DESCRIPTION("STMicroelectronics STM32 Timer Trigger driver"); MODULE_LICENSE("GPL v2"); |
