| // 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, |
| struct iio_trigger *trig) |
| { |
| 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); |
| |
| /* Force disable master mode */ |
| if (stm32_timer_is_trgo2_name(trig->name)) |
| regmap_update_bits(priv->regmap, TIM_CR2, TIM_CR2_MMS2, 0); |
| else |
| regmap_update_bits(priv->regmap, TIM_CR2, TIM_CR2_MMS, 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, trig); |
| } 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"); |