|
@@ -15,6 +15,7 @@
|
|
|
#include <linux/platform_device.h>
|
|
|
|
|
|
#define MAX_TRIGGERS 6
|
|
|
+#define MAX_VALIDS 5
|
|
|
|
|
|
/* List the triggers created by each timer */
|
|
|
static const void *triggers_table[][MAX_TRIGGERS] = {
|
|
@@ -32,12 +33,29 @@ static const void *triggers_table[][MAX_TRIGGERS] = {
|
|
|
{ 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;
|
|
|
};
|
|
|
|
|
|
static int stm32_timer_start(struct stm32_timer_trigger *priv,
|
|
@@ -180,8 +198,7 @@ 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);
|
|
|
u32 cr2;
|
|
|
|
|
|
regmap_read(priv->regmap, TIM_CR2, &cr2);
|
|
@@ -194,8 +211,7 @@ 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);
|
|
|
int i;
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(master_mode_table); i++) {
|
|
@@ -275,6 +291,216 @@ 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 const struct iio_info stm32_trigger_info = {
|
|
|
+ .driver_module = THIS_MODULE,
|
|
|
+ .read_raw = stm32_counter_read_raw,
|
|
|
+ .write_raw = stm32_counter_write_raw
|
|
|
+};
|
|
|
+
|
|
|
+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),
|
|
|
+ {}
|
|
|
+};
|
|
|
+
|
|
|
+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->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
|
|
@@ -299,10 +525,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 +543,7 @@ 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];
|
|
|
|
|
|
ret = stm32_setup_iio_triggers(priv);
|
|
|
if (ret)
|