diff options
Diffstat (limited to 'drivers/counter/stm32-timer-cnt.c')
-rw-r--r-- | drivers/counter/stm32-timer-cnt.c | 347 |
1 files changed, 192 insertions, 155 deletions
diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c index 3eafccec3beb..9bf20a5d6bda 100644 --- a/drivers/counter/stm32-timer-cnt.c +++ b/drivers/counter/stm32-timer-cnt.c @@ -8,11 +8,12 @@ * */ #include <linux/counter.h> -#include <linux/iio/iio.h> -#include <linux/iio/types.h> #include <linux/mfd/stm32-timers.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> +#include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> +#include <linux/types.h> #define TIM_CCMR_CCXS (BIT(8) | BIT(0)) #define TIM_CCMR_MASK (TIM_CCMR_CC1S | TIM_CCMR_CC2S | \ @@ -20,37 +21,32 @@ #define TIM_CCER_MASK (TIM_CCER_CC1P | TIM_CCER_CC1NP | \ TIM_CCER_CC2P | TIM_CCER_CC2NP) +struct stm32_timer_regs { + u32 cr1; + u32 cnt; + u32 smcr; + u32 arr; +}; + struct stm32_timer_cnt { - struct counter_device counter; struct regmap *regmap; struct clk *clk; - u32 ceiling; + u32 max_arr; + bool enabled; + struct stm32_timer_regs bak; }; -/** - * enum stm32_count_function - enumerates stm32 timer counter encoder modes - * @STM32_COUNT_SLAVE_MODE_DISABLED: counts on internal clock when CEN=1 - * @STM32_COUNT_ENCODER_MODE_1: counts TI1FP1 edges, depending on TI2FP2 level - * @STM32_COUNT_ENCODER_MODE_2: counts TI2FP2 edges, depending on TI1FP1 level - * @STM32_COUNT_ENCODER_MODE_3: counts on both TI1FP1 and TI2FP2 edges - */ -enum stm32_count_function { - STM32_COUNT_SLAVE_MODE_DISABLED = -1, - STM32_COUNT_ENCODER_MODE_1, - STM32_COUNT_ENCODER_MODE_2, - STM32_COUNT_ENCODER_MODE_3, -}; - -static enum counter_count_function stm32_count_functions[] = { - [STM32_COUNT_ENCODER_MODE_1] = COUNTER_COUNT_FUNCTION_QUADRATURE_X2_A, - [STM32_COUNT_ENCODER_MODE_2] = COUNTER_COUNT_FUNCTION_QUADRATURE_X2_B, - [STM32_COUNT_ENCODER_MODE_3] = COUNTER_COUNT_FUNCTION_QUADRATURE_X4, +static const enum counter_function stm32_count_functions[] = { + COUNTER_FUNCTION_INCREASE, + COUNTER_FUNCTION_QUADRATURE_X2_A, + COUNTER_FUNCTION_QUADRATURE_X2_B, + COUNTER_FUNCTION_QUADRATURE_X4, }; static int stm32_count_read(struct counter_device *counter, - struct counter_count *count, unsigned long *val) + struct counter_count *count, u64 *val) { - struct stm32_timer_cnt *const priv = counter->priv; + struct stm32_timer_cnt *const priv = counter_priv(counter); u32 cnt; regmap_read(priv->regmap, TIM_CNT, &cnt); @@ -60,61 +56,67 @@ static int stm32_count_read(struct counter_device *counter, } static int stm32_count_write(struct counter_device *counter, - struct counter_count *count, - const unsigned long val) + struct counter_count *count, const u64 val) { - struct stm32_timer_cnt *const priv = counter->priv; + struct stm32_timer_cnt *const priv = counter_priv(counter); + u32 ceiling; - if (val > priv->ceiling) + regmap_read(priv->regmap, TIM_ARR, &ceiling); + if (val > ceiling) return -EINVAL; return regmap_write(priv->regmap, TIM_CNT, val); } -static int stm32_count_function_get(struct counter_device *counter, - struct counter_count *count, - size_t *function) +static int stm32_count_function_read(struct counter_device *counter, + struct counter_count *count, + enum counter_function *function) { - struct stm32_timer_cnt *const priv = counter->priv; + struct stm32_timer_cnt *const priv = counter_priv(counter); u32 smcr; regmap_read(priv->regmap, TIM_SMCR, &smcr); switch (smcr & TIM_SMCR_SMS) { - case 1: - *function = STM32_COUNT_ENCODER_MODE_1; + case TIM_SMCR_SMS_SLAVE_MODE_DISABLED: + *function = COUNTER_FUNCTION_INCREASE; + return 0; + case TIM_SMCR_SMS_ENCODER_MODE_1: + *function = COUNTER_FUNCTION_QUADRATURE_X2_A; return 0; - case 2: - *function = STM32_COUNT_ENCODER_MODE_2; + case TIM_SMCR_SMS_ENCODER_MODE_2: + *function = COUNTER_FUNCTION_QUADRATURE_X2_B; return 0; - case 3: - *function = STM32_COUNT_ENCODER_MODE_3; + case TIM_SMCR_SMS_ENCODER_MODE_3: + *function = COUNTER_FUNCTION_QUADRATURE_X4; return 0; + default: + return -EINVAL; } - - return -EINVAL; } -static int stm32_count_function_set(struct counter_device *counter, - struct counter_count *count, - size_t function) +static int stm32_count_function_write(struct counter_device *counter, + struct counter_count *count, + enum counter_function function) { - struct stm32_timer_cnt *const priv = counter->priv; + struct stm32_timer_cnt *const priv = counter_priv(counter); u32 cr1, sms; switch (function) { - case STM32_COUNT_ENCODER_MODE_1: - sms = 1; + case COUNTER_FUNCTION_INCREASE: + sms = TIM_SMCR_SMS_SLAVE_MODE_DISABLED; break; - case STM32_COUNT_ENCODER_MODE_2: - sms = 2; + case COUNTER_FUNCTION_QUADRATURE_X2_A: + sms = TIM_SMCR_SMS_ENCODER_MODE_1; break; - case STM32_COUNT_ENCODER_MODE_3: - sms = 3; + case COUNTER_FUNCTION_QUADRATURE_X2_B: + sms = TIM_SMCR_SMS_ENCODER_MODE_2; break; - default: - sms = 0; + case COUNTER_FUNCTION_QUADRATURE_X4: + sms = TIM_SMCR_SMS_ENCODER_MODE_3; break; + default: + return -EINVAL; } /* Store enable status */ @@ -122,10 +124,6 @@ static int stm32_count_function_set(struct counter_device *counter, regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0); - /* TIMx_ARR register shouldn't be buffered (ARPE=0) */ - regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, 0); - regmap_write(priv->regmap, TIM_ARR, priv->ceiling); - regmap_update_bits(priv->regmap, TIM_SMCR, TIM_SMCR_SMS, sms); /* Make sure that registers are updated */ @@ -137,78 +135,66 @@ static int stm32_count_function_set(struct counter_device *counter, return 0; } -static ssize_t stm32_count_direction_read(struct counter_device *counter, +static int stm32_count_direction_read(struct counter_device *counter, struct counter_count *count, - void *private, char *buf) + enum counter_count_direction *direction) { - struct stm32_timer_cnt *const priv = counter->priv; - const char *direction; + struct stm32_timer_cnt *const priv = counter_priv(counter); u32 cr1; regmap_read(priv->regmap, TIM_CR1, &cr1); - direction = (cr1 & TIM_CR1_DIR) ? "backward" : "forward"; + *direction = (cr1 & TIM_CR1_DIR) ? COUNTER_COUNT_DIRECTION_BACKWARD : + COUNTER_COUNT_DIRECTION_FORWARD; - return scnprintf(buf, PAGE_SIZE, "%s\n", direction); + return 0; } -static ssize_t stm32_count_ceiling_read(struct counter_device *counter, - struct counter_count *count, - void *private, char *buf) +static int stm32_count_ceiling_read(struct counter_device *counter, + struct counter_count *count, u64 *ceiling) { - struct stm32_timer_cnt *const priv = counter->priv; + struct stm32_timer_cnt *const priv = counter_priv(counter); u32 arr; regmap_read(priv->regmap, TIM_ARR, &arr); - return snprintf(buf, PAGE_SIZE, "%u\n", arr); + *ceiling = arr; + + return 0; } -static ssize_t stm32_count_ceiling_write(struct counter_device *counter, - struct counter_count *count, - void *private, - const char *buf, size_t len) +static int stm32_count_ceiling_write(struct counter_device *counter, + struct counter_count *count, u64 ceiling) { - struct stm32_timer_cnt *const priv = counter->priv; - unsigned int ceiling; - int ret; + struct stm32_timer_cnt *const priv = counter_priv(counter); - ret = kstrtouint(buf, 0, &ceiling); - if (ret) - return ret; + if (ceiling > priv->max_arr) + return -ERANGE; /* TIMx_ARR register shouldn't be buffered (ARPE=0) */ regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, 0); regmap_write(priv->regmap, TIM_ARR, ceiling); - priv->ceiling = ceiling; - return len; + return 0; } -static ssize_t stm32_count_enable_read(struct counter_device *counter, - struct counter_count *count, - void *private, char *buf) +static int stm32_count_enable_read(struct counter_device *counter, + struct counter_count *count, u8 *enable) { - struct stm32_timer_cnt *const priv = counter->priv; + struct stm32_timer_cnt *const priv = counter_priv(counter); u32 cr1; regmap_read(priv->regmap, TIM_CR1, &cr1); - return scnprintf(buf, PAGE_SIZE, "%d\n", (bool)(cr1 & TIM_CR1_CEN)); + *enable = cr1 & TIM_CR1_CEN; + + return 0; } -static ssize_t stm32_count_enable_write(struct counter_device *counter, - struct counter_count *count, - void *private, - const char *buf, size_t len) +static int stm32_count_enable_write(struct counter_device *counter, + struct counter_count *count, u8 enable) { - struct stm32_timer_cnt *const priv = counter->priv; - int err; + struct stm32_timer_cnt *const priv = counter_priv(counter); u32 cr1; - bool enable; - - err = kstrtobool(buf, &enable); - if (err) - return err; if (enable) { regmap_read(priv->regmap, TIM_CR1, &cr1); @@ -224,77 +210,70 @@ static ssize_t stm32_count_enable_write(struct counter_device *counter, clk_disable(priv->clk); } - return len; -} + /* Keep enabled state to properly handle low power states */ + priv->enabled = enable; -static const struct counter_count_ext stm32_count_ext[] = { - { - .name = "direction", - .read = stm32_count_direction_read, - }, - { - .name = "enable", - .read = stm32_count_enable_read, - .write = stm32_count_enable_write - }, - { - .name = "ceiling", - .read = stm32_count_ceiling_read, - .write = stm32_count_ceiling_write - }, -}; + return 0; +} -enum stm32_synapse_action { - STM32_SYNAPSE_ACTION_NONE, - STM32_SYNAPSE_ACTION_BOTH_EDGES +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), }; -static enum counter_synapse_action stm32_synapse_actions[] = { - [STM32_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE, - [STM32_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES +static const enum counter_synapse_action stm32_synapse_actions[] = { + COUNTER_SYNAPSE_ACTION_NONE, + COUNTER_SYNAPSE_ACTION_BOTH_EDGES }; -static int stm32_action_get(struct counter_device *counter, - struct counter_count *count, - struct counter_synapse *synapse, - size_t *action) +static int stm32_action_read(struct counter_device *counter, + struct counter_count *count, + struct counter_synapse *synapse, + enum counter_synapse_action *action) { - size_t function; + enum counter_function function; int err; - /* Default action mode (e.g. STM32_COUNT_SLAVE_MODE_DISABLED) */ - *action = STM32_SYNAPSE_ACTION_NONE; - - err = stm32_count_function_get(counter, count, &function); + err = stm32_count_function_read(counter, count, &function); if (err) - return 0; + return err; switch (function) { - case STM32_COUNT_ENCODER_MODE_1: + case COUNTER_FUNCTION_INCREASE: + /* counts on internal clock when CEN=1 */ + *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) - *action = STM32_SYNAPSE_ACTION_BOTH_EDGES; - break; - case STM32_COUNT_ENCODER_MODE_2: + *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) - *action = STM32_SYNAPSE_ACTION_BOTH_EDGES; - break; - case STM32_COUNT_ENCODER_MODE_3: + *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 = STM32_SYNAPSE_ACTION_BOTH_EDGES; - break; + *action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES; + return 0; + default: + return -EINVAL; } - - return 0; } static const struct counter_ops stm32_timer_cnt_ops = { .count_read = stm32_count_read, .count_write = stm32_count_write, - .function_get = stm32_count_function_get, - .function_set = stm32_count_function_set, - .action_get = stm32_action_get, + .function_read = stm32_count_function_read, + .function_write = stm32_count_function_write, + .action_read = stm32_action_read, }; static struct counter_signal stm32_signals[] = { @@ -337,31 +316,87 @@ 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; if (IS_ERR_OR_NULL(ddata)) return -EINVAL; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) + counter = devm_counter_alloc(dev, sizeof(*priv)); + if (!counter) return -ENOMEM; + priv = counter_priv(counter); + priv->regmap = ddata->regmap; priv->clk = ddata->clk; - priv->ceiling = ddata->max_arr; + priv->max_arr = ddata->max_arr; + + counter->name = dev_name(dev); + counter->parent = dev; + counter->ops = &stm32_timer_cnt_ops; + counter->counts = &stm32_counts; + counter->num_counts = 1; + counter->signals = stm32_signals; + counter->num_signals = ARRAY_SIZE(stm32_signals); - priv->counter.name = dev_name(dev); - priv->counter.parent = dev; - priv->counter.ops = &stm32_timer_cnt_ops; - priv->counter.counts = &stm32_counts; - priv->counter.num_counts = 1; - priv->counter.signals = stm32_signals; - priv->counter.num_signals = ARRAY_SIZE(stm32_signals); - priv->counter.priv = priv; + platform_set_drvdata(pdev, priv); /* Register Counter device */ - return devm_counter_register(dev, &priv->counter); + ret = devm_counter_add(dev, counter); + if (ret < 0) + dev_err_probe(dev, ret, "Failed to add counter\n"); + + return ret; } +static int __maybe_unused stm32_timer_cnt_suspend(struct device *dev) +{ + struct stm32_timer_cnt *priv = dev_get_drvdata(dev); + + /* Only take care of enabled counter: don't disturb other MFD child */ + if (priv->enabled) { + /* Backup registers that may get lost in low power mode */ + regmap_read(priv->regmap, TIM_SMCR, &priv->bak.smcr); + regmap_read(priv->regmap, TIM_ARR, &priv->bak.arr); + regmap_read(priv->regmap, TIM_CNT, &priv->bak.cnt); + regmap_read(priv->regmap, TIM_CR1, &priv->bak.cr1); + + /* Disable the counter */ + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0); + clk_disable(priv->clk); + } + + return pinctrl_pm_select_sleep_state(dev); +} + +static int __maybe_unused stm32_timer_cnt_resume(struct device *dev) +{ + struct stm32_timer_cnt *priv = dev_get_drvdata(dev); + int ret; + + ret = pinctrl_pm_select_default_state(dev); + if (ret) + return ret; + + if (priv->enabled) { + clk_enable(priv->clk); + + /* Restore registers that may have been lost */ + regmap_write(priv->regmap, TIM_SMCR, priv->bak.smcr); + regmap_write(priv->regmap, TIM_ARR, priv->bak.arr); + regmap_write(priv->regmap, TIM_CNT, priv->bak.cnt); + + /* Also re-enables the counter */ + regmap_write(priv->regmap, TIM_CR1, priv->bak.cr1); + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(stm32_timer_cnt_pm_ops, stm32_timer_cnt_suspend, + stm32_timer_cnt_resume); + static const struct of_device_id stm32_timer_cnt_of_match[] = { { .compatible = "st,stm32-timer-counter", }, {}, @@ -373,6 +408,7 @@ static struct platform_driver stm32_timer_cnt_driver = { .driver = { .name = "stm32-timer-counter", .of_match_table = stm32_timer_cnt_of_match, + .pm = &stm32_timer_cnt_pm_ops, }, }; module_platform_driver(stm32_timer_cnt_driver); @@ -381,3 +417,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); |