diff options
Diffstat (limited to 'drivers/iio/trigger/stm32-timer-trigger.c')
-rw-r--r-- | drivers/iio/trigger/stm32-timer-trigger.c | 494 |
1 files changed, 471 insertions, 23 deletions
diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c index 994b96d19750..d22bc56dd9fc 100644 --- a/drivers/iio/trigger/stm32-timer-trigger.c +++ b/drivers/iio/trigger/stm32-timer-trigger.c @@ -14,33 +14,58 @@ #include <linux/module.h> #include <linux/platform_device.h> -#define MAX_TRIGGERS 6 +#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_CH1, TIM1_CH2, TIM1_CH3, TIM1_CH4,}, + { 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_CH1, TIM8_CH2, TIM8_CH3, TIM8_CH4,}, + { TIM8_TRGO, TIM8_TRGO2, TIM8_CH1, TIM8_CH2, TIM8_CH3, TIM8_CH4,}, { TIM9_TRGO, TIM9_CH1, TIM9_CH2,}, { }, /* timer 10 */ { }, /* timer 11 */ { TIM12_TRGO, TIM12_CH1, TIM12_CH2,}, }; +/* 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,}, + { }, /* timer 10 */ + { }, /* timer 11 */ + { TIM4_TRGO, TIM5_TRGO,}, +}; + 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; }; +static bool stm32_timer_is_trgo2_name(const char *name) +{ + return !!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; @@ -84,7 +109,12 @@ static int stm32_timer_start(struct stm32_timer_trigger *priv, regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, TIM_CR1_ARPE); /* Force master mode to update mode */ - regmap_update_bits(priv->regmap, TIM_CR2, TIM_CR2_MMS, 0x20); + 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); @@ -132,7 +162,7 @@ static ssize_t stm32_tt_store_frequency(struct device *dev, if (freq == 0) { stm32_timer_stop(priv); } else { - ret = stm32_timer_start(priv, freq); + ret = stm32_timer_start(priv, trig, freq); if (ret) return ret; } @@ -152,10 +182,10 @@ static ssize_t stm32_tt_read_frequency(struct device *dev, regmap_read(priv->regmap, TIM_PSC, &psc); regmap_read(priv->regmap, TIM_ARR, &arr); - if (psc && arr && (cr1 & TIM_CR1_CEN)) { + if (cr1 & TIM_CR1_CEN) { freq = (unsigned long long)clk_get_rate(priv->clk); - do_div(freq, psc); - do_div(freq, arr); + do_div(freq, psc + 1); + do_div(freq, arr + 1); } return sprintf(buf, "%d\n", (unsigned int)freq); @@ -165,6 +195,9 @@ 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", @@ -173,19 +206,32 @@ static char *master_mode_table[] = { "OC1REF", "OC2REF", "OC3REF", - "OC4REF" + "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 iio_dev *indio_dev = dev_to_iio_dev(dev); - struct stm32_timer_trigger *priv = iio_priv(indio_dev); + 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); - cr2 = (cr2 & TIM_CR2_MMS) >> TIM_CR2_MMS_SHIFT; + + 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]); } @@ -194,15 +240,26 @@ static ssize_t stm32_tt_store_master_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct stm32_timer_trigger *priv = iio_priv(indio_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; - for (i = 0; i < ARRAY_SIZE(master_mode_table); 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, - TIM_CR2_MMS, i << TIM_CR2_MMS_SHIFT); + 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); @@ -213,8 +270,31 @@ static ssize_t stm32_tt_store_master_mode(struct device *dev, return -EINVAL; } -static IIO_CONST_ATTR(master_mode_available, - "reset enable update compare_pulse OC1REF OC2REF OC3REF OC4REF"); +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, @@ -224,7 +304,7 @@ static IIO_DEVICE_ATTR(master_mode, 0660, static struct attribute *stm32_trigger_attrs[] = { &iio_dev_attr_sampling_frequency.dev_attr.attr, &iio_dev_attr_master_mode.dev_attr.attr, - &iio_const_attr_master_mode_available.dev_attr.attr, + &iio_dev_attr_master_mode_available.dev_attr.attr, NULL, }; @@ -248,6 +328,12 @@ static int stm32_setup_iio_triggers(struct stm32_timer_trigger *priv) while (cur && *cur) { struct iio_trigger *trig; + 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) @@ -261,7 +347,7 @@ static int stm32_setup_iio_triggers(struct stm32_timer_trigger *priv) * should only be available on trgo trigger which * is always the first in the list. */ - if (cur == priv->triggers) + if (cur == priv->triggers || cur_is_trgo2) trig->dev.groups = stm32_trigger_attr_groups; iio_trigger_set_drvdata(trig, priv); @@ -275,6 +361,347 @@ static int stm32_setup_iio_triggers(struct stm32_timer_trigger *priv) 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); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + { + u32 cnt; + + regmap_read(priv->regmap, TIM_CNT, &cnt); + *val = cnt; + + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_SCALE: + { + u32 smcr; + + regmap_read(priv->regmap, TIM_SMCR, &smcr); + smcr &= TIM_SMCR_SMS; + + *val = 1; + *val2 = 0; + + /* in quadrature case scale = 0.25 */ + if (smcr == 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); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + regmap_write(priv->regmap, TIM_CNT, val); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* fixed scale */ + return -EINVAL; + } + + 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 = { + .driver_module = THIS_MODULE, + .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 ? 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); + + if (sms < 0) + return sms; + + 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; + + regmap_read(priv->regmap, TIM_SMCR, &smcr); + smcr &= TIM_SMCR_SMS; + + return smcr - 1; +} + +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 mode) +{ + struct stm32_timer_trigger *priv = iio_priv(indio_dev); + + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_DIR, mode); + + return 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); +} + +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; + + regmap_write(priv->regmap, TIM_ARR, preset); + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, TIM_CR1_ARPE); + + 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_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 @@ -288,6 +715,20 @@ bool is_stm32_timer_trigger(struct iio_trigger *trig) } 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; @@ -299,10 +740,15 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev) if (of_property_read_u32(dev->of_node, "reg", &index)) return -EINVAL; - if (index >= ARRAY_SIZE(triggers_table)) + if (index >= ARRAY_SIZE(triggers_table) || + index >= ARRAY_SIZE(valids_table)) return -EINVAL; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + /* Create an IIO device only if we have triggers to be validated */ + if (*valids_table[index]) + priv = stm32_setup_counter_device(dev); + else + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -312,6 +758,8 @@ 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 = valids_table[index]; + stm32_timer_detect_trgo2(priv); ret = stm32_setup_iio_triggers(priv); if (ret) |