From d1caa99055382c91b57244343020ea37c4fa4d09 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Tue, 13 Dec 2016 15:33:32 +0100 Subject: iio: adc: add support for Allwinner SoCs ADC The Allwinner SoCs all have an ADC that can also act as a touchscreen controller and a thermal sensor. This patch adds the ADC driver which is based on the MFD for the same SoCs ADC. This also registers the thermal adc channel in the iio map array so iio_hwmon could use it without modifying the Device Tree. This registers the driver in the thermal framework. The thermal sensor requires the IP to be in touchscreen mode to return correct values. Therefore, if the user is continuously reading the ADC channel(s), the thermal framework in which the thermal sensor is registered will switch the IP in touchscreen mode to get a temperature value and requires a delay of 100ms (because of the mode switching), then the ADC will switch back to ADC mode and requires also a delay of 100ms. If the ADC readings are critical to user and the SoC temperature is not, this driver is capable of not registering the thermal sensor in the thermal framework and thus, "quicken" the ADC readings. This driver probes on three different platform_device_id to take into account slight differences (registers bit and temperature computation) between Allwinner SoCs ADCs. Signed-off-by: Quentin Schulz Acked-by: Maxime Ripard Acked-by: Jonathan Cameron Acked-for-MFD-by: Lee Jones Signed-off-by: Jonathan Cameron --- include/linux/mfd/sun4i-gpadc.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux/mfd') diff --git a/include/linux/mfd/sun4i-gpadc.h b/include/linux/mfd/sun4i-gpadc.h index d7a29f246d64..509e736d27fb 100644 --- a/include/linux/mfd/sun4i-gpadc.h +++ b/include/linux/mfd/sun4i-gpadc.h @@ -28,6 +28,7 @@ #define SUN4I_GPADC_CTRL1_TP_MODE_EN BIT(4) #define SUN4I_GPADC_CTRL1_TP_ADC_SELECT BIT(3) #define SUN4I_GPADC_CTRL1_ADC_CHAN_SELECT(x) (GENMASK(2, 0) & (x)) +#define SUN4I_GPADC_CTRL1_ADC_CHAN_MASK GENMASK(2, 0) /* TP_CTRL1 bits for sun6i SOCs */ #define SUN6I_GPADC_CTRL1_TOUCH_PAN_CALI_EN BIT(7) @@ -35,6 +36,7 @@ #define SUN6I_GPADC_CTRL1_TP_MODE_EN BIT(5) #define SUN6I_GPADC_CTRL1_TP_ADC_SELECT BIT(4) #define SUN6I_GPADC_CTRL1_ADC_CHAN_SELECT(x) (GENMASK(3, 0) & BIT(x)) +#define SUN6I_GPADC_CTRL1_ADC_CHAN_MASK GENMASK(3, 0) #define SUN4I_GPADC_CTRL2 0x08 -- cgit v1.2.3 From 808a8b73772c6ac7d999c0508d2f757831cd83ca Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Wed, 5 Apr 2017 11:06:30 +0200 Subject: iio: adc: sun4i-gpadc-iio: add support for A33 thermal sensor This adds support for the Allwinner A33 thermal sensor. Unlike the A10, A13 and A31, the Allwinner A33 only has one channel which is dedicated to the thermal sensor. Moreover, its thermal sensor does not generate interruptions, thus we only need to directly read the register storing the temperature value. The MFD used by the A10, A13 and A31, was created to avoid breaking the DT binding, but since the nodes for the ADC weren't there for the A33, it is not needed. Though the A33 does not have an internal ADC, it has a thermal sensor which shares the same registers with GPADC of the already supported SoCs and almost the same bits, for the same purpose (thermal sensor). The thermal sensor behaves exactly the same (except the presence of interrupts or not) on the different SoCs. Signed-off-by: Quentin Schulz Acked-by: Lee Jones Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 2 +- drivers/iio/adc/sun4i-gpadc-iio.c | 100 ++++++++++++++++++++++++++++++++++++-- include/linux/mfd/sun4i-gpadc.h | 4 ++ 3 files changed, 102 insertions(+), 4 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 1e25ef009130..f924fa0d4fd6 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -604,7 +604,7 @@ config STX104 config SUN4I_GPADC tristate "Support for the Allwinner SoCs GPADC" depends on IIO - depends on MFD_SUN4I_GPADC + depends on MFD_SUN4I_GPADC || MACH_SUN8I depends on THERMAL || !THERMAL_OF help Say yes here to build support for Allwinner (A10, A13 and A31) SoCs diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c index 4c0167562965..b23527309088 100644 --- a/drivers/iio/adc/sun4i-gpadc-iio.c +++ b/drivers/iio/adc/sun4i-gpadc-iio.c @@ -85,6 +85,12 @@ static const struct gpadc_data sun6i_gpadc_data = { .adc_chan_mask = SUN6I_GPADC_CTRL1_ADC_CHAN_MASK, }; +static const struct gpadc_data sun8i_a33_gpadc_data = { + .temp_offset = -1662, + .temp_scale = 162, + .tp_mode_en = SUN8I_GPADC_CTRL1_CHOP_TEMP_EN, +}; + struct sun4i_gpadc_iio { struct iio_dev *indio_dev; struct completion completion; @@ -96,6 +102,7 @@ struct sun4i_gpadc_iio { unsigned int temp_data_irq; atomic_t ignore_temp_data_irq; const struct gpadc_data *data; + bool no_irq; /* prevents concurrent reads of temperature and ADC */ struct mutex mutex; }; @@ -138,6 +145,23 @@ static const struct iio_chan_spec sun4i_gpadc_channels_no_temp[] = { SUN4I_GPADC_ADC_CHANNEL(3, "adc_chan3"), }; +static const struct iio_chan_spec sun8i_a33_gpadc_channels[] = { + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .datasheet_name = "temp_adc", + }, +}; + +static const struct regmap_config sun4i_gpadc_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .fast_io = true, +}; + static int sun4i_prepare_for_irq(struct iio_dev *indio_dev, int channel, unsigned int irq) { @@ -247,6 +271,17 @@ static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val) { struct sun4i_gpadc_iio *info = iio_priv(indio_dev); + if (info->no_irq) { + pm_runtime_get_sync(indio_dev->dev.parent); + + regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, val); + + pm_runtime_mark_last_busy(indio_dev->dev.parent); + pm_runtime_put_autosuspend(indio_dev->dev.parent); + + return 0; + } + return sun4i_gpadc_read(indio_dev, 0, val, info->temp_data_irq); } @@ -454,6 +489,58 @@ static int sun4i_irq_init(struct platform_device *pdev, const char *name, return 0; } +static const struct of_device_id sun4i_gpadc_of_id[] = { + { + .compatible = "allwinner,sun8i-a33-ths", + .data = &sun8i_a33_gpadc_data, + }, + { /* sentinel */ } +}; + +static int sun4i_gpadc_probe_dt(struct platform_device *pdev, + struct iio_dev *indio_dev) +{ + struct sun4i_gpadc_iio *info = iio_priv(indio_dev); + const struct of_device_id *of_dev; + struct thermal_zone_device *tzd; + struct resource *mem; + void __iomem *base; + int ret; + + of_dev = of_match_device(sun4i_gpadc_of_id, &pdev->dev); + if (!of_dev) + return -ENODEV; + + info->no_irq = true; + info->data = (struct gpadc_data *)of_dev->data; + indio_dev->num_channels = ARRAY_SIZE(sun8i_a33_gpadc_channels); + indio_dev->channels = sun8i_a33_gpadc_channels; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(base)) + return PTR_ERR(base); + + info->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &sun4i_gpadc_regmap_config); + if (IS_ERR(info->regmap)) { + ret = PTR_ERR(info->regmap); + dev_err(&pdev->dev, "failed to init regmap: %d\n", ret); + return ret; + } + + if (!IS_ENABLED(CONFIG_THERMAL_OF)) + return 0; + + tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, info, + &sun4i_ts_tz_ops); + if (IS_ERR(tzd)) + dev_err(&pdev->dev, "could not register thermal sensor: %ld\n", + PTR_ERR(tzd)); + + return PTR_ERR_OR_ZERO(tzd); +} + static int sun4i_gpadc_probe_mfd(struct platform_device *pdev, struct iio_dev *indio_dev) { @@ -462,6 +549,7 @@ static int sun4i_gpadc_probe_mfd(struct platform_device *pdev, dev_get_drvdata(pdev->dev.parent); int ret; + info->no_irq = false; info->regmap = sun4i_gpadc_dev->regmap; indio_dev->num_channels = ARRAY_SIZE(sun4i_gpadc_channels); @@ -561,7 +649,11 @@ static int sun4i_gpadc_probe(struct platform_device *pdev) indio_dev->info = &sun4i_gpadc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; - ret = sun4i_gpadc_probe_mfd(pdev, indio_dev); + if (pdev->dev.of_node) + ret = sun4i_gpadc_probe_dt(pdev, indio_dev); + else + ret = sun4i_gpadc_probe_mfd(pdev, indio_dev); + if (ret) return ret; @@ -580,7 +672,7 @@ static int sun4i_gpadc_probe(struct platform_device *pdev) return 0; err_map: - if (IS_ENABLED(CONFIG_THERMAL_OF)) + if (!info->no_irq && IS_ENABLED(CONFIG_THERMAL_OF)) iio_map_array_unregister(indio_dev); pm_runtime_put(&pdev->dev); @@ -592,10 +684,11 @@ err_map: static int sun4i_gpadc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct sun4i_gpadc_iio *info = iio_priv(indio_dev); pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); - if (IS_ENABLED(CONFIG_THERMAL_OF)) + if (!info->no_irq && IS_ENABLED(CONFIG_THERMAL_OF)) iio_map_array_unregister(indio_dev); return 0; @@ -611,6 +704,7 @@ static const struct platform_device_id sun4i_gpadc_id[] = { static struct platform_driver sun4i_gpadc_driver = { .driver = { .name = "sun4i-gpadc-iio", + .of_match_table = sun4i_gpadc_of_id, .pm = &sun4i_gpadc_pm_ops, }, .id_table = sun4i_gpadc_id, diff --git a/include/linux/mfd/sun4i-gpadc.h b/include/linux/mfd/sun4i-gpadc.h index 509e736d27fb..139872c2e0fe 100644 --- a/include/linux/mfd/sun4i-gpadc.h +++ b/include/linux/mfd/sun4i-gpadc.h @@ -38,6 +38,10 @@ #define SUN6I_GPADC_CTRL1_ADC_CHAN_SELECT(x) (GENMASK(3, 0) & BIT(x)) #define SUN6I_GPADC_CTRL1_ADC_CHAN_MASK GENMASK(3, 0) +/* TP_CTRL1 bits for sun8i SoCs */ +#define SUN8I_GPADC_CTRL1_CHOP_TEMP_EN BIT(8) +#define SUN8I_GPADC_CTRL1_GPADC_CALI_EN BIT(7) + #define SUN4I_GPADC_CTRL2 0x08 #define SUN4I_GPADC_CTRL2_TP_SENSITIVE_ADJUST(x) ((GENMASK(3, 0) & (x)) << 28) -- cgit v1.2.3 From 4adec7da0536a345d901d7ba55b6c93a14eeeaff Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Tue, 4 Apr 2017 09:47:51 +0200 Subject: iio: stm32 trigger: Add quadrature encoder device One of the features of STM32 trigger hardware block is a quadrature encoder that can counts up/down depending of the levels and edges of the selected external pins. This patch allow to read/write the counter, get it direction, set/get quadrature modes and get scale factor. When counting up preset value is the limit of the counter. When counting down the counter start from preset value down to 0. This preset value could be set/get by using /sys/bus/iio/devices/iio:deviceX/in_count0_preset attribute. Signed-off-by: Benjamin Gaignard Reviewed-by: William Breathitt Gray Signed-off-by: Jonathan Cameron --- .../ABI/testing/sysfs-bus-iio-timer-stm32 | 46 +++- drivers/iio/trigger/stm32-timer-trigger.c | 244 ++++++++++++++++++++- include/linux/mfd/stm32-timers.h | 2 + 3 files changed, 282 insertions(+), 10 deletions(-) (limited to 'include/linux/mfd') diff --git a/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32 b/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32 index 6534a60037ff..a55ad44446c5 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32 +++ b/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32 @@ -3,11 +3,15 @@ KernelVersion: 4.11 Contact: benjamin.gaignard@st.com Description: Reading returns the list possible master modes which are: - - "reset" : The UG bit from the TIMx_EGR register is used as trigger output (TRGO). - - "enable" : The Counter Enable signal CNT_EN is used as trigger output. + - "reset" : The UG bit from the TIMx_EGR register is + used as trigger output (TRGO). + - "enable" : The Counter Enable signal CNT_EN is used + as trigger output. - "update" : The update event is selected as trigger output. - For instance a master timer can then be used as a prescaler for a slave timer. - - "compare_pulse" : The trigger output send a positive pulse when the CC1IF flag is to be set. + For instance a master timer can then be used + as a prescaler for a slave timer. + - "compare_pulse" : The trigger output send a positive pulse + when the CC1IF flag is to be set. - "OC1REF" : OC1REF signal is used as trigger output. - "OC2REF" : OC2REF signal is used as trigger output. - "OC3REF" : OC3REF signal is used as trigger output. @@ -27,3 +31,37 @@ Description: Reading returns the current sampling frequency. Writing an value different of 0 set and start sampling. Writing 0 stop sampling. + +What: /sys/bus/iio/devices/iio:deviceX/in_count0_preset +KernelVersion: 4.12 +Contact: benjamin.gaignard@st.com +Description: + Reading returns the current preset value. + Writing sets the preset value. + When counting up the counter starts from 0 and fires an + event when reach preset value. + When counting down the counter start from preset value + and fire event when reach 0. + +What: /sys/bus/iio/devices/iio:deviceX/in_count_quadrature_mode_available +KernelVersion: 4.12 +Contact: benjamin.gaignard@st.com +Description: + Reading returns the list possible quadrature modes. + +What: /sys/bus/iio/devices/iio:deviceX/in_count0_quadrature_mode +KernelVersion: 4.12 +Contact: benjamin.gaignard@st.com +Description: + Configure the device counter quadrature modes: + channel_A: + Encoder A input servers as the count input and B as + the UP/DOWN direction control input. + + channel_B: + Encoder B input serves as the count input and A as + the UP/DOWN direction control input. + + quadrature: + Encoder A and B inputs are mixed to get direction + and count with a scale of 0.25. diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c index 994b96d19750..7db904cae926 100644 --- a/drivers/iio/trigger/stm32-timer-trigger.c +++ b/drivers/iio/trigger/stm32-timer-trigger.c @@ -15,6 +15,7 @@ #include #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) diff --git a/include/linux/mfd/stm32-timers.h b/include/linux/mfd/stm32-timers.h index d0300045f04a..4a0abbc10ef6 100644 --- a/include/linux/mfd/stm32-timers.h +++ b/include/linux/mfd/stm32-timers.h @@ -21,6 +21,7 @@ #define TIM_CCMR1 0x18 /* Capt/Comp 1 Mode Reg */ #define TIM_CCMR2 0x1C /* Capt/Comp 2 Mode Reg */ #define TIM_CCER 0x20 /* Capt/Comp Enable Reg */ +#define TIM_CNT 0x24 /* Counter */ #define TIM_PSC 0x28 /* Prescaler */ #define TIM_ARR 0x2c /* Auto-Reload Register */ #define TIM_CCR1 0x34 /* Capt/Comp Register 1 */ @@ -30,6 +31,7 @@ #define TIM_BDTR 0x44 /* Break and Dead-Time Reg */ #define TIM_CR1_CEN BIT(0) /* Counter Enable */ +#define TIM_CR1_DIR BIT(4) /* Counter Direction */ #define TIM_CR1_ARPE BIT(7) /* Auto-reload Preload Ena */ #define TIM_CR2_MMS (BIT(4) | BIT(5) | BIT(6)) /* Master mode selection */ #define TIM_SMCR_SMS (BIT(0) | BIT(1) | BIT(2)) /* Slave mode selection */ -- cgit v1.2.3