| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Copyright (C) STMicroelectronics 2016 | |
| * | |
| * Author: Benjamin Gaignard <benjamin.gaignard@st.com> | |
| * | |
| */ | |
| #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/module.h> | |
| #include <linux/platform_device.h> | |
| #include <linux/of_device.h> | |
| #define MAX_TRIGGERS 7 | |
| #define MAX_VALIDS 5 | |
| /* List the triggers created by each timer */ | |
| static const void *triggers_table[][MAX_TRIGGERS] = { | |
| { TIM1_TRGO, TIM1_TRGO2, TIM1_CH1, TIM1_CH2, TIM1_CH3, TIM1_CH4,}, | |
| { TIM2_TRGO, TIM2_CH1, TIM2_CH2, TIM2_CH3, TIM2_CH4,}, | |
| { TIM3_TRGO, TIM3_CH1, TIM3_CH2, TIM3_CH3, TIM3_CH4,}, | |
| { TIM4_TRGO, TIM4_CH1, TIM4_CH2, TIM4_CH3, TIM4_CH4,}, | |
| { TIM5_TRGO, TIM5_CH1, TIM5_CH2, TIM5_CH3, TIM5_CH4,}, | |
| { TIM6_TRGO,}, | |
| { TIM7_TRGO,}, | |
| { TIM8_TRGO, TIM8_TRGO2, TIM8_CH1, TIM8_CH2, TIM8_CH3, TIM8_CH4,}, | |
| { TIM9_TRGO, TIM9_CH1, TIM9_CH2,}, | |
| { TIM10_OC1,}, | |
| { TIM11_OC1,}, | |
| { TIM12_TRGO, TIM12_CH1, TIM12_CH2,}, | |
| { TIM13_OC1,}, | |
| { TIM14_OC1,}, | |
| { TIM15_TRGO,}, | |
| { TIM16_OC1,}, | |
| { TIM17_OC1,}, | |
| }; | |
| /* List the triggers accepted by each timer */ | |
| static const void *valids_table[][MAX_VALIDS] = { | |
| { TIM5_TRGO, TIM2_TRGO, TIM3_TRGO, TIM4_TRGO,}, | |
| { TIM1_TRGO, TIM8_TRGO, TIM3_TRGO, TIM4_TRGO,}, | |
| { TIM1_TRGO, TIM2_TRGO, TIM5_TRGO, TIM4_TRGO,}, | |
| { TIM1_TRGO, TIM2_TRGO, TIM3_TRGO, TIM8_TRGO,}, | |
| { TIM2_TRGO, TIM3_TRGO, TIM4_TRGO, TIM8_TRGO,}, | |
| { }, /* timer 6 */ | |
| { }, /* timer 7 */ | |
| { TIM1_TRGO, TIM2_TRGO, TIM4_TRGO, TIM5_TRGO,}, | |
| { TIM2_TRGO, TIM3_TRGO, TIM10_OC1, TIM11_OC1,}, | |
| { }, /* timer 10 */ | |
| { }, /* timer 11 */ | |
| { TIM4_TRGO, TIM5_TRGO, TIM13_OC1, TIM14_OC1,}, | |
| }; | |
| static const void *stm32h7_valids_table[][MAX_VALIDS] = { | |
| { TIM15_TRGO, TIM2_TRGO, TIM3_TRGO, TIM4_TRGO,}, | |
| { TIM1_TRGO, TIM8_TRGO, TIM3_TRGO, TIM4_TRGO,}, | |
| { TIM1_TRGO, TIM2_TRGO, TIM15_TRGO, TIM4_TRGO,}, | |
| { TIM1_TRGO, TIM2_TRGO, TIM3_TRGO, TIM8_TRGO,}, | |
| { TIM1_TRGO, TIM8_TRGO, TIM3_TRGO, TIM4_TRGO,}, | |
| { }, /* timer 6 */ | |
| { }, /* timer 7 */ | |
| { TIM1_TRGO, TIM2_TRGO, TIM4_TRGO, TIM5_TRGO,}, | |
| { }, /* timer 9 */ | |
| { }, /* timer 10 */ | |
| { }, /* timer 11 */ | |
| { TIM4_TRGO, TIM5_TRGO, TIM13_OC1, TIM14_OC1,}, | |
| { }, /* timer 13 */ | |
| { }, /* timer 14 */ | |
| { TIM1_TRGO, TIM3_TRGO, TIM16_OC1, TIM17_OC1,}, | |
| { }, /* timer 16 */ | |
| { }, /* timer 17 */ | |
| }; | |
| struct stm32_timer_trigger { | |
| struct device *dev; | |
| struct regmap *regmap; | |
| struct clk *clk; | |
| u32 max_arr; | |
| const void *triggers; | |
| const void *valids; | |
| bool has_trgo2; | |
| }; | |
| struct stm32_timer_trigger_cfg { | |
| const void *(*valids_table)[MAX_VALIDS]; | |
| const unsigned int num_valids_table; | |
| }; | |
| static bool stm32_timer_is_trgo2_name(const char *name) | |
| { | |
| return !!strstr(name, "trgo2"); | |
| } | |
| static bool stm32_timer_is_trgo_name(const char *name) | |
| { | |
| return (!!strstr(name, "trgo") && !strstr(name, "trgo2")); | |
| } | |
| static int stm32_timer_start(struct stm32_timer_trigger *priv, | |
| struct iio_trigger *trig, | |
| unsigned int frequency) | |
| { | |
| unsigned long long prd, div; | |
| int prescaler = 0; | |
| u32 ccer, cr1; | |
| /* Period and prescaler values depends of clock rate */ | |
| div = (unsigned long long)clk_get_rate(priv->clk); | |
| do_div(div, frequency); | |
| prd = div; | |
| /* | |
| * Increase prescaler value until we get a result that fit | |
| * with auto reload register maximum value. | |
| */ | |
| while (div > priv->max_arr) { | |
| prescaler++; | |
| div = prd; | |
| do_div(div, (prescaler + 1)); | |
| } | |
| prd = div; | |
| if (prescaler > MAX_TIM_PSC) { | |
| dev_err(priv->dev, "prescaler exceeds the maximum value\n"); | |
| return -EINVAL; | |
| } | |
| /* Check if nobody else use the timer */ | |
| regmap_read(priv->regmap, TIM_CCER, &ccer); | |
| if (ccer & TIM_CCER_CCXE) | |
| return -EBUSY; | |
| regmap_read(priv->regmap, TIM_CR1, &cr1); | |
| if (!(cr1 & TIM_CR1_CEN)) | |
| clk_enable(priv->clk); | |
| 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); | |
| /* Force master mode to update mode */ | |
| if (stm32_timer_is_trgo2_name(trig->name)) | |
| regmap_update_bits(priv->regmap, TIM_CR2, TIM_CR2_MMS2, | |
| 0x2 << TIM_CR2_MMS2_SHIFT); | |
| else | |
| regmap_update_bits(priv->regmap, TIM_CR2, TIM_CR2_MMS, | |
| 0x2 << TIM_CR2_MMS_SHIFT); | |
| /* Make sure that registers are updated */ | |
| regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG); | |
| /* Enable controller */ | |
| regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN); | |
| return 0; | |
| } | |
| static void stm32_timer_stop(struct stm32_timer_trigger *priv) | |
| { | |
| u32 ccer, cr1; | |
| 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); | |
| /* 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_write(priv->regmap, TIM_PSC, 0); | |
| regmap_write(priv->regmap, TIM_ARR, 0); | |
| /* Make sure that registers are updated */ | |
| regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG); | |
| } | |
| static ssize_t stm32_tt_store_frequency(struct device *dev, | |
| struct device_attribute *attr, | |
| const char *buf, size_t len) | |
| { | |
| struct iio_trigger *trig = to_iio_trigger(dev); | |
| struct stm32_timer_trigger *priv = iio_trigger_get_drvdata(trig); | |
| unsigned int freq; | |
| int ret; | |
| ret = kstrtouint(buf, 10, &freq); | |
| if (ret) | |
| return ret; | |
| if (freq == 0) { | |
| stm32_timer_stop(priv); | |
| } else { | |
| ret = stm32_timer_start(priv, trig, freq); | |
| if (ret) | |
| return ret; | |
| } | |
| return len; | |
| } | |
| static ssize_t stm32_tt_read_frequency(struct device *dev, | |
| struct device_attribute *attr, char *buf) | |
| { | |
| struct iio_trigger *trig = to_iio_trigger(dev); | |
| struct stm32_timer_trigger *priv = iio_trigger_get_drvdata(trig); | |
| u32 psc, arr, cr1; | |
| unsigned long long freq = 0; | |
| regmap_read(priv->regmap, TIM_CR1, &cr1); | |
| regmap_read(priv->regmap, TIM_PSC, &psc); | |
| regmap_read(priv->regmap, TIM_ARR, &arr); | |
| if (cr1 & TIM_CR1_CEN) { | |
| freq = (unsigned long long)clk_get_rate(priv->clk); | |
| do_div(freq, psc + 1); | |
| do_div(freq, arr + 1); | |
| } | |
| return sprintf(buf, "%d\n", (unsigned int)freq); | |
| } | |
| static IIO_DEV_ATTR_SAMP_FREQ(0660, | |
| stm32_tt_read_frequency, | |
| stm32_tt_store_frequency); | |
| #define MASTER_MODE_MAX 7 | |
| #define MASTER_MODE2_MAX 15 | |
| static char *master_mode_table[] = { | |
| "reset", | |
| "enable", | |
| "update", | |
| "compare_pulse", | |
| "OC1REF", | |
| "OC2REF", | |
| "OC3REF", | |
| "OC4REF", | |
| /* Master mode selection 2 only */ | |
| "OC5REF", | |
| "OC6REF", | |
| "compare_pulse_OC4REF", | |
| "compare_pulse_OC6REF", | |
| "compare_pulse_OC4REF_r_or_OC6REF_r", | |
| "compare_pulse_OC4REF_r_or_OC6REF_f", | |
| "compare_pulse_OC5REF_r_or_OC6REF_r", | |
| "compare_pulse_OC5REF_r_or_OC6REF_f", | |
| }; | |
| static ssize_t stm32_tt_show_master_mode(struct device *dev, | |
| struct device_attribute *attr, | |
| char *buf) | |
| { | |
| struct stm32_timer_trigger *priv = dev_get_drvdata(dev); | |
| struct iio_trigger *trig = to_iio_trigger(dev); | |
| u32 cr2; | |
| regmap_read(priv->regmap, TIM_CR2, &cr2); | |
| if (stm32_timer_is_trgo2_name(trig->name)) | |
| cr2 = (cr2 & TIM_CR2_MMS2) >> TIM_CR2_MMS2_SHIFT; | |
| else | |
| cr2 = (cr2 & TIM_CR2_MMS) >> TIM_CR2_MMS_SHIFT; | |
| return snprintf(buf, PAGE_SIZE, "%s\n", master_mode_table[cr2]); | |
| } | |
| static ssize_t stm32_tt_store_master_mode(struct device *dev, | |
| struct device_attribute *attr, | |
| const char *buf, size_t len) | |
| { | |
| 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; | |
| if (stm32_timer_is_trgo2_name(trig->name)) { | |
| mask = TIM_CR2_MMS2; | |
| shift = TIM_CR2_MMS2_SHIFT; | |
| master_mode_max = MASTER_MODE2_MAX; | |
| } else { | |
| mask = TIM_CR2_MMS; | |
| shift = TIM_CR2_MMS_SHIFT; | |
| master_mode_max = MASTER_MODE_MAX; | |
| } | |
| for (i = 0; i <= master_mode_max; i++) { | |
| if (!strncmp(master_mode_table[i], buf, | |
| strlen(master_mode_table[i]))) { | |
| 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; | |
| } | |
| } | |
| return -EINVAL; | |
| } | |
| static ssize_t stm32_tt_show_master_mode_avail(struct device *dev, | |
| struct device_attribute *attr, | |
| char *buf) | |
| { | |
| struct iio_trigger *trig = to_iio_trigger(dev); | |
| unsigned int i, master_mode_max; | |
| size_t len = 0; | |
| if (stm32_timer_is_trgo2_name(trig->name)) | |
| master_mode_max = MASTER_MODE2_MAX; | |
| else | |
| master_mode_max = MASTER_MODE_MAX; | |
| for (i = 0; i <= master_mode_max; i++) | |
| len += scnprintf(buf + len, PAGE_SIZE - len, | |
| "%s ", master_mode_table[i]); | |
| /* replace trailing space by newline */ | |
| buf[len - 1] = '\n'; | |
| return len; | |
| } | |
| static IIO_DEVICE_ATTR(master_mode_available, 0444, | |
| stm32_tt_show_master_mode_avail, NULL, 0); | |
| static IIO_DEVICE_ATTR(master_mode, 0660, | |
| stm32_tt_show_master_mode, | |
| stm32_tt_store_master_mode, | |
| 0); | |
| static struct attribute *stm32_trigger_attrs[] = { | |
| &iio_dev_attr_sampling_frequency.dev_attr.attr, | |
| &iio_dev_attr_master_mode.dev_attr.attr, | |
| &iio_dev_attr_master_mode_available.dev_attr.attr, | |
| NULL, | |
| }; | |
| static const struct attribute_group stm32_trigger_attr_group = { | |
| .attrs = stm32_trigger_attrs, | |
| }; | |
| static const struct attribute_group *stm32_trigger_attr_groups[] = { | |
| &stm32_trigger_attr_group, | |
| NULL, | |
| }; | |
| static const struct iio_trigger_ops timer_trigger_ops = { | |
| }; | |
| static int stm32_setup_iio_triggers(struct stm32_timer_trigger *priv) | |
| { | |
| int ret; | |
| const char * const *cur = priv->triggers; | |
| while (cur && *cur) { | |
| struct iio_trigger *trig; | |
| bool cur_is_trgo = stm32_timer_is_trgo_name(*cur); | |
| bool cur_is_trgo2 = stm32_timer_is_trgo2_name(*cur); | |
| if (cur_is_trgo2 && !priv->has_trgo2) { | |
| cur++; | |
| continue; | |
| } | |
| trig = devm_iio_trigger_alloc(priv->dev, "%s", *cur); | |
| if (!trig) | |
| return -ENOMEM; | |
| trig->dev.parent = priv->dev->parent; | |
| trig->ops = &timer_trigger_ops; | |
| /* | |
| * sampling frequency and master mode attributes | |
| * should only be available on trgo/trgo2 triggers | |
| */ | |
| if (cur_is_trgo || cur_is_trgo2) | |
| trig->dev.groups = stm32_trigger_attr_groups; | |
| iio_trigger_set_drvdata(trig, priv); | |
| ret = devm_iio_trigger_register(priv->dev, trig); | |
| if (ret) | |
| return ret; | |
| cur++; | |
| } | |
| return 0; | |
| } | |
| static int stm32_counter_read_raw(struct iio_dev *indio_dev, | |
| struct iio_chan_spec const *chan, | |
| int *val, int *val2, long mask) | |
| { | |
| struct stm32_timer_trigger *priv = iio_priv(indio_dev); | |
| u32 dat; | |
| switch (mask) { | |
| case IIO_CHAN_INFO_RAW: | |
| regmap_read(priv->regmap, TIM_CNT, &dat); | |
| *val = dat; | |
| return IIO_VAL_INT; | |
| case IIO_CHAN_INFO_ENABLE: | |
| regmap_read(priv->regmap, TIM_CR1, &dat); | |
| *val = (dat & TIM_CR1_CEN) ? 1 : 0; | |
| return IIO_VAL_INT; | |
| case IIO_CHAN_INFO_SCALE: | |
| regmap_read(priv->regmap, TIM_SMCR, &dat); | |
| dat &= TIM_SMCR_SMS; | |
| *val = 1; | |
| *val2 = 0; | |
| /* in quadrature case scale = 0.25 */ | |
| if (dat == 3) | |
| *val2 = 2; | |
| return IIO_VAL_FRACTIONAL_LOG2; | |
| } | |
| return -EINVAL; | |
| } | |
| static int stm32_counter_write_raw(struct iio_dev *indio_dev, | |
| struct iio_chan_spec const *chan, | |
| int val, int val2, long mask) | |
| { | |
| struct stm32_timer_trigger *priv = iio_priv(indio_dev); | |
| u32 dat; | |
| switch (mask) { | |
| case IIO_CHAN_INFO_RAW: | |
| return regmap_write(priv->regmap, TIM_CNT, val); | |
| case IIO_CHAN_INFO_SCALE: | |
| /* fixed scale */ | |
| return -EINVAL; | |
| case IIO_CHAN_INFO_ENABLE: | |
| 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); | |
| } else { | |
| regmap_read(priv->regmap, TIM_CR1, &dat); | |
| regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, | |
| 0); | |
| if (dat & TIM_CR1_CEN) | |
| clk_disable(priv->clk); | |
| } | |
| return 0; | |
| } | |
| return -EINVAL; | |
| } | |
| static int stm32_counter_validate_trigger(struct iio_dev *indio_dev, | |
| struct iio_trigger *trig) | |
| { | |
| struct stm32_timer_trigger *priv = iio_priv(indio_dev); | |
| const char * const *cur = priv->valids; | |
| unsigned int i = 0; | |
| if (!is_stm32_timer_trigger(trig)) | |
| return -EINVAL; | |
| while (cur && *cur) { | |
| if (!strncmp(trig->name, *cur, strlen(trig->name))) { | |
| regmap_update_bits(priv->regmap, | |
| TIM_SMCR, TIM_SMCR_TS, | |
| i << TIM_SMCR_TS_SHIFT); | |
| return 0; | |
| } | |
| cur++; | |
| i++; | |
| } | |
| return -EINVAL; | |
| } | |
| static const struct iio_info stm32_trigger_info = { | |
| .validate_trigger = stm32_counter_validate_trigger, | |
| .read_raw = stm32_counter_read_raw, | |
| .write_raw = stm32_counter_write_raw | |
| }; | |
| static const char *const stm32_trigger_modes[] = { | |
| "trigger", | |
| }; | |
| static int stm32_set_trigger_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, TIM_SMCR_SMS); | |
| return 0; | |
| } | |
| static int stm32_get_trigger_mode(struct iio_dev *indio_dev, | |
| const struct iio_chan_spec *chan) | |
| { | |
| struct stm32_timer_trigger *priv = iio_priv(indio_dev); | |
| u32 smcr; | |
| regmap_read(priv->regmap, TIM_SMCR, &smcr); | |
| return (smcr & TIM_SMCR_SMS) == TIM_SMCR_SMS ? 0 : -EINVAL; | |
| } | |
| static const struct iio_enum stm32_trigger_mode_enum = { | |
| .items = stm32_trigger_modes, | |
| .num_items = ARRAY_SIZE(stm32_trigger_modes), | |
| .set = stm32_set_trigger_mode, | |
| .get = stm32_get_trigger_mode | |
| }; | |
| static const char *const stm32_enable_modes[] = { | |
| "always", | |
| "gated", | |
| "triggered", | |
| }; | |
| static int stm32_enable_mode2sms(int mode) | |
| { | |
| switch (mode) { | |
| case 0: | |
| return 0; | |
| case 1: | |
| return 5; | |
| case 2: | |
| return 6; | |
| } | |
| return -EINVAL; | |
| } | |
| static int stm32_set_enable_mode(struct iio_dev *indio_dev, | |
| const struct iio_chan_spec *chan, | |
| unsigned int mode) | |
| { | |
| struct stm32_timer_trigger *priv = iio_priv(indio_dev); | |
| int sms = stm32_enable_mode2sms(mode); | |
| u32 val; | |
| if (sms < 0) | |
| return sms; | |
| /* | |
| * 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); | |
| } | |
| regmap_update_bits(priv->regmap, TIM_SMCR, TIM_SMCR_SMS, sms); | |
| return 0; | |
| } | |
| static int stm32_sms2enable_mode(int mode) | |
| { | |
| switch (mode) { | |
| case 0: | |
| return 0; | |
| case 5: | |
| return 1; | |
| case 6: | |
| return 2; | |
| } | |
| return -EINVAL; | |
| } | |
| static int stm32_get_enable_mode(struct iio_dev *indio_dev, | |
| const struct iio_chan_spec *chan) | |
| { | |
| struct stm32_timer_trigger *priv = iio_priv(indio_dev); | |
| u32 smcr; | |
| regmap_read(priv->regmap, TIM_SMCR, &smcr); | |
| smcr &= TIM_SMCR_SMS; | |
| return stm32_sms2enable_mode(smcr); | |
| } | |
| static const struct iio_enum stm32_enable_mode_enum = { | |
| .items = stm32_enable_modes, | |
| .num_items = ARRAY_SIZE(stm32_enable_modes), | |
| .set = stm32_set_enable_mode, | |
| .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, | |
| char *buf) | |
| { | |
| struct stm32_timer_trigger *priv = iio_priv(indio_dev); | |
| u32 arr; | |
| regmap_read(priv->regmap, TIM_ARR, &arr); | |
| return snprintf(buf, PAGE_SIZE, "%u\n", arr); | |
| } | |
| static ssize_t stm32_count_set_preset(struct iio_dev *indio_dev, | |
| uintptr_t private, | |
| const struct iio_chan_spec *chan, | |
| const char *buf, size_t len) | |
| { | |
| struct stm32_timer_trigger *priv = iio_priv(indio_dev); | |
| unsigned int preset; | |
| int ret; | |
| ret = kstrtouint(buf, 0, &preset); | |
| if (ret) | |
| return ret; | |
| /* 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, preset); | |
| return len; | |
| } | |
| static const struct iio_chan_spec_ext_info stm32_trigger_count_info[] = { | |
| { | |
| .name = "preset", | |
| .shared = IIO_SEPARATE, | |
| .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("trigger_mode", IIO_SEPARATE, &stm32_trigger_mode_enum), | |
| IIO_ENUM_AVAILABLE("trigger_mode", &stm32_trigger_mode_enum), | |
| {} | |
| }; | |
| static const struct iio_chan_spec stm32_trigger_channel = { | |
| .type = IIO_COUNT, | |
| .channel = 0, | |
| .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
| BIT(IIO_CHAN_INFO_ENABLE) | | |
| BIT(IIO_CHAN_INFO_SCALE), | |
| .ext_info = stm32_trigger_count_info, | |
| .indexed = 1 | |
| }; | |
| static struct stm32_timer_trigger *stm32_setup_counter_device(struct device *dev) | |
| { | |
| struct iio_dev *indio_dev; | |
| int ret; | |
| indio_dev = devm_iio_device_alloc(dev, | |
| sizeof(struct stm32_timer_trigger)); | |
| if (!indio_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) | |
| return NULL; | |
| return iio_priv(indio_dev); | |
| } | |
| /** | |
| * is_stm32_timer_trigger | |
| * @trig: trigger to be checked | |
| * | |
| * return true if the trigger is a valid stm32 iio timer trigger | |
| * either return false | |
| */ | |
| bool is_stm32_timer_trigger(struct iio_trigger *trig) | |
| { | |
| return (trig->ops == &timer_trigger_ops); | |
| } | |
| EXPORT_SYMBOL(is_stm32_timer_trigger); | |
| static void stm32_timer_detect_trgo2(struct stm32_timer_trigger *priv) | |
| { | |
| u32 val; | |
| /* | |
| * 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_read(priv->regmap, TIM_CR2, &val); | |
| regmap_update_bits(priv->regmap, TIM_CR2, TIM_CR2_MMS2, 0); | |
| priv->has_trgo2 = !!val; | |
| } | |
| static int stm32_timer_trigger_probe(struct platform_device *pdev) | |
| { | |
| struct device *dev = &pdev->dev; | |
| struct stm32_timer_trigger *priv; | |
| struct stm32_timers *ddata = dev_get_drvdata(pdev->dev.parent); | |
| const struct stm32_timer_trigger_cfg *cfg; | |
| unsigned int index; | |
| int ret; | |
| if (of_property_read_u32(dev->of_node, "reg", &index)) | |
| return -EINVAL; | |
| cfg = (const struct stm32_timer_trigger_cfg *) | |
| of_match_device(dev->driver->of_match_table, dev)->data; | |
| 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]) | |
| priv = stm32_setup_counter_device(dev); | |
| else | |
| priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | |
| if (!priv) | |
| return -ENOMEM; | |
| priv->dev = dev; | |
| priv->regmap = ddata->regmap; | |
| priv->clk = ddata->clk; | |
| priv->max_arr = ddata->max_arr; | |
| priv->triggers = triggers_table[index]; | |
| priv->valids = cfg->valids_table[index]; | |
| stm32_timer_detect_trgo2(priv); | |
| ret = stm32_setup_iio_triggers(priv); | |
| if (ret) | |
| return ret; | |
| platform_set_drvdata(pdev, priv); | |
| return 0; | |
| } | |
| static const struct stm32_timer_trigger_cfg stm32_timer_trg_cfg = { | |
| .valids_table = valids_table, | |
| .num_valids_table = ARRAY_SIZE(valids_table), | |
| }; | |
| static const struct stm32_timer_trigger_cfg stm32h7_timer_trg_cfg = { | |
| .valids_table = stm32h7_valids_table, | |
| .num_valids_table = ARRAY_SIZE(stm32h7_valids_table), | |
| }; | |
| static const struct of_device_id stm32_trig_of_match[] = { | |
| { | |
| .compatible = "st,stm32-timer-trigger", | |
| .data = (void *)&stm32_timer_trg_cfg, | |
| }, { | |
| .compatible = "st,stm32h7-timer-trigger", | |
| .data = (void *)&stm32h7_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, | |
| .driver = { | |
| .name = "stm32-timer-trigger", | |
| .of_match_table = stm32_trig_of_match, | |
| }, | |
| }; | |
| module_platform_driver(stm32_timer_trigger_driver); | |
| MODULE_ALIAS("platform: stm32-timer-trigger"); | |
| MODULE_DESCRIPTION("STMicroelectronics STM32 Timer Trigger driver"); | |
| MODULE_LICENSE("GPL v2"); |