Re: [PATCH v2 8/8] iio: counter: Add support for STM32 LPTimer
From: Benjamin Gaignard
Date: Tue Jun 27 2017 - 04:35:43 EST
2017-06-26 22:29 GMT+02:00 William Breathitt Gray <vilhelm.gray@xxxxxxxxx>:
> On Sat, Jun 24, 2017 at 09:35:39PM +0100, Jonathan Cameron wrote:
>>On Wed, 21 Jun 2017 16:30:15 +0200
>>Fabrice Gasnier <fabrice.gasnier@xxxxxx> wrote:
>>
>>> Add support for STM32 Low-Power Timer, that can be used as counter
>>> or quadrature encoder.
>>>
>>> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@xxxxxx>
>>Hmm. Sometime I'm going to ask you guys to document how all these different
>>components fit together. Far too many ways of cooking the same dish with
>>some of these ST parts ;)
>>
>>I've cc'd William. You already have Benjamin. Hopefully they also
>>have time to cast their eyes over this patch as it would be very helpful.
>>We are still defining new ABI for these devices so good to have more eyes
>>than for a normal patch.
>>Jonathan
>>> ---
>>> Changes in v2:
>>> - s/Low Power/Low-Power
>>> - update few comments
>>> ---
>>> .../ABI/testing/sysfs-bus-iio-lptimer-stm32 | 57 +++
>>> drivers/iio/counter/Kconfig | 9 +
>>> drivers/iio/counter/Makefile | 1 +
>>> drivers/iio/counter/stm32-lptimer-cnt.c | 383 +++++++++++++++++++++
>>> 4 files changed, 450 insertions(+)
>>> create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
>>> create mode 100644 drivers/iio/counter/stm32-lptimer-cnt.c
>>>
>>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32 b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
>>> new file mode 100644
>>> index 0000000..ad2cc63
>>> --- /dev/null
>>> +++ b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
>>> @@ -0,0 +1,57 @@
>>> +What: /sys/bus/iio/devices/iio:deviceX/in_count0_preset
>>> +KernelVersion: 4.13
>>> +Contact: fabrice.gasnier@xxxxxx
>>> +Description:
>>> + Reading returns the current preset value. Writing sets the
>>> + preset value. Encoder counts continuously from 0 to preset
>>> + value, depending on direction (up/down).
>>Some of these are generic now and used by several parts. Time we started
>>thinking about a more generic file. sysfs-bus-iio-counter
>>> +
>>> +What: /sys/bus/iio/devices/iio:deviceX/in_count_quadrature_mode_available
>>> +KernelVersion: 4.13
>>> +Contact: fabrice.gasnier@xxxxxx
>>> +Description:
>>> + Reading returns the list possible quadrature modes.
>>> +
>>> +What: /sys/bus/iio/devices/iio:deviceX/in_count0_quadrature_mode
>>> +KernelVersion: 4.13
>>> +Contact: fabrice.gasnier@xxxxxx
>>> +Description:
>>> + Configure the device counter quadrature modes:
>>> + - non-quadrature:
>>> + Encoder IN1 input servers as the count input (up
>>> + direction).
>>> + - quadrature:
>>> + Encoder IN1 and IN2 inputs are mixed to get direction
>>> + and count.
>>Don't suppose we can call them A and B in common with labelling on many encoders?
>>Also makes this documentation same as for the 104 device.
>>> +
>>> +What: /sys/bus/iio/devices/iio:deviceX/in_count_polarity_available
>>> +KernelVersion: 4.13
>>> +Contact: fabrice.gasnier@xxxxxx
>>> +Description:
>>> + Reading returns the list possible active edges.
>>> +
>>> +What: /sys/bus/iio/devices/iio:deviceX/in_count0_polarity
>>> +KernelVersion: 4.13
>>> +Contact: fabrice.gasnier@xxxxxx
>>> +Description:
>>> + Configure the device encoder/counter active edge:
>>> + - rising-edge
>>> + - falling-edge
>>> + - both-edges
>>For both edges, I believe we last supported this with scale.
>>So can we have both edges for the non quadrature? If so your scale reported
>>is not taking this into account.
>>> +
>>> + In non-quadrature mode, device counts up on active edge.
>>> + In quadrature mode, encoder counting scenarios are as follows:
>>> + ----------------------------------------------------------------
>>> + | Active | Level on | IN1 signal | IN2 signal |
>>> + | edge | opposite |------------------------------------------
>>> + | | signal | Rising | Falling | Rising | Falling |
>>> + ----------------------------------------------------------------
>>> + | Rising | High -> | Down | - | Up | - |
>>> + | edge | Low -> | Up | - | Down | - |
>>> + ----------------------------------------------------------------
>>> + | Falling | High -> | - | Up | - | Down |
>>> + | edge | Low -> | - | Down | - | Up |
>>> + ----------------------------------------------------------------
>>> + | Both | High -> | Down | Up | Up | Down |
>>> + | edges | Low -> | Up | Down | Down | Up |
>>> + ----------------------------------------------------------------
>>Last case was definitely done with scale for the 104 counter - not that it
>>is detailed enough here to cover the other two cases.
>>It might make sense to add any new interface to that one as well to become
>>the favoured way of setting or reading this...
>>
>>Anyone else have a better idea?
>
> When we introduced the first counter device driver to the iio subsystem
> we anticipated the arrival of subsequent counter device drivers to
> elucidate the common functionality of these kinds of devices; I believe
> the new counter device drivers added to the kernel in these past few
> releases have provided us with enough to see the trends in these
> devices. Congolmerating the attributes we see repeated among these
> drivers into a common sysfs-bus-iio-counter interface would be
> beneficial for future authors.
>
> Specific devices are bound to require specific attributes, but there are
> certain functionalities that all counters share -- determining the
> essence of a counter is key to defining a useful generic counter
> interface. For example, a good number of counter devices I've
> encountered have some sort of "preset" functionality; but whereas one
> device may treat the "preset" value as a count ceiling, another may
> treat it as a count floor. Knowing where to draw the line of defining
> what the "preset" attribute represents is the problem.
Maybe we should have min, max and reset values attribut instead of using
preset ?
>
> Allow me to propose the following generic definition of an idealized
> counter device: a counter is a device that accumulates the state changes
> of one or more signal lines to a defined value. This generic definition
> should guide us in defining a proper generic iio counter interface.
>
> Referring to the generic description, we know that every counter device
> will have a "value" attribute where the accumulation of the signal lines
> are held. Furthermore, the accumulation operation must be defined: some
> devices count up, some down, and some either; an attribute can be used
> to select the accumulation operation.
>
> The accumulation operation in these devices must have a trigger
> condition (i.e. state change). This is where we've had trouble in the
> past trying to deal with quadrature modes. I propose that we separate
> the idea of quadrature modes from the concept of state changes.
>
> Quadrature counters in my mind are simply regular counters that
> accumulate state changes on multiple wires at the same time to a single
> value; the fact that the signals are quadrature (90 degrees offset) is
> of no consequence: reversal of direction is simply a change of the
> accumulation operation in most devices to indicate the change to
> counting is the opposite direction.
>
> I don't particularly like how the "scale" attribute is used to hide the
> quadrature modes (x1, x2, and x4) of the 104-QUAD-8 device: the various
> quadrature modes indicate how state changes are interpreted by the
> device, but "scale" loses such information by representing it as simply
> a scaled accumulated value which could overlap with another counting
> mode.
>
> For example, traditionally quadrature modes are defined as such: x1
> counts the rising edges of channel A in the forward direction and the
> falling edges of channel A in the backward direction, x2 counts both
> rising and falling edges of channel A, and x4 counts both rising and
> falling edges of both channel A and channel B. Now suppose a device
> allows another possible mode where just the rising edges of both channel
> A and channel B are, or a mode where just the falling edges of both
> channel A and channel B, or a mode where only channle B is counted
> instead of channel A, etc.? In these modes, the accumulated value may
> match closely to one of the traditional quadrature modes, but the
> "scale" attribute does not display this information.
>
> The reason I point out these hypothetical modes is because I don't
> think the iio counter interface should be so tied to quadrature encoder
> functionality: although, position tracking is a useful functionality of
> a counter, a counter should be able to count arbitrary signals based on
> well defined state changes. This will allow counter drivers to be
> written to serve a diverse variety of devices. That is why the focus
> should be on what constitutes a "state change."
>
> So let us separate what we have been calling "quadrature mode" into a
> more generic interface of "signal lines" and "state changes." A "signal
> line" would be the channels associated with a single accumulation
> "value," such as channel A and channel B. Each signal line can then have
> an associated "state change" mode (i.e. the trigger for the accumulation
> operation) which can be set to the desired mode such as "None," "Rising
> Edge," "Falling Edge," "Both," etc. In this way, multiple signal lines
> (even more than 2) can be associated to an accumulation value, and
> configured to the desired mode (e.g. quadrature mode) to handle whatever
> kind of data is represented by those incoming signal lines.
Name it "Signal lines" sound good for me, I would prefer "active state" rather
than "state changes" but it just wording so with a good documentation
it could works.
If you propose patch (and documentation) for that I could convert my
stm32 timers
driver to this interface.
>
> To summarize: the generic iio counter interface should feature
> accumulation value attributes, which shall each have an associated
> accumulation operation attribute and respective number of signal line
> attributes associated with the accumulation value, where each signal
> line has an associated state change mode which defines the condition
> on the respective signal line that triggers the accumulation operation.
>
> Let me know what you think. I'm worried if this interface would be too
> generic or cumbersome to use -- but I'm also worried about the counter
> interface becoming too specific to quadrature signals and getting tied
> down such a specialized use case.
Define on which edges counter is active seems generic and no specifically
link to quadrature devices so it is not a problem for me.
>
> William Breathitt Gray
>
>>> diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
>>> index b37e5fc..474e1ac 100644
>>> --- a/drivers/iio/counter/Kconfig
>>> +++ b/drivers/iio/counter/Kconfig
>>> @@ -21,4 +21,13 @@ config 104_QUAD_8
>>> The base port addresses for the devices may be configured via the base
>>> array module parameter.
>>>
>>> +config STM32_LPTIMER_CNT
>>> + tristate "STM32 LP Timer encoder counter driver"
>>> + depends on MFD_STM32_LPTIMER || COMPILE_TEST
>>> + help
>>> + Select this option to enable STM32 Low-Power Timer quadrature encoder
>>> + and counter driver.
>>> +
>>> + To compile this driver as a module, choose M here: the
>>> + module will be called stm32-lptimer-cnt.
>>> endmenu
>>> diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
>>> index 007e884..1b9a896 100644
>>> --- a/drivers/iio/counter/Makefile
>>> +++ b/drivers/iio/counter/Makefile
>>> @@ -5,3 +5,4 @@
>>> # When adding new entries keep the list in alphabetical order
>>>
>>> obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o
>>> +obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.o
>>> diff --git a/drivers/iio/counter/stm32-lptimer-cnt.c b/drivers/iio/counter/stm32-lptimer-cnt.c
>>> new file mode 100644
>>> index 0000000..1c5909b
>>> --- /dev/null
>>> +++ b/drivers/iio/counter/stm32-lptimer-cnt.c
>>> @@ -0,0 +1,383 @@
>>> +/*
>>> + * STM32 Low-Power Timer Encoder and Counter driver
>>> + *
>>> + * Copyright (C) STMicroelectronics 2017
>>> + *
>>> + * Author: Fabrice Gasnier <fabrice.gasnier@xxxxxx>
>>> + *
>>> + * Inspired by 104-quad-8 and stm32-timer-trigger drivers.
>>> + *
>>> + * License terms: GNU General Public License (GPL), version 2
>>> + */
>>> +
>>> +#include <linux/bitfield.h>
>>> +#include <linux/iio/iio.h>
>>> +#include <linux/mfd/stm32-lptimer.h>
>>> +#include <linux/module.h>
>>> +#include <linux/platform_device.h>
>>> +
>>> +struct stm32_lptim_cnt {
>>> + struct device *dev;
>>> + struct regmap *regmap;
>>> + struct clk *clk;
>>> + u32 preset;
>>> + u32 polarity;
>>> + u32 quadrature_mode;
>>> +};
>>> +
>>> +static int stm32_lptim_is_enabled(struct stm32_lptim_cnt *priv)
>>> +{
>>> + u32 val;
>>> + int ret;
>>> +
>>> + ret = regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + return FIELD_GET(STM32_LPTIM_ENABLE, val);
>>> +}
>>> +
>>> +static int stm32_lptim_set_enable_state(struct stm32_lptim_cnt *priv,
>>> + int enable)
>>> +{
>>> + int ret;
>>> + u32 val;
>>> +
>>> + val = FIELD_PREP(STM32_LPTIM_ENABLE, enable);
>>> + ret = regmap_write(priv->regmap, STM32_LPTIM_CR, val);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + if (!enable) {
>>> + clk_disable(priv->clk);
>>> + return 0;
>>> + }
>>> +
>>> + /* LP timer must be enabled before writing CMP & ARR */
>>> + ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, priv->preset);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, 0);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + /* ensure CMP & ARR registers are properly written */
>>> + ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val,
>>> + (val & STM32_LPTIM_CMPOK_ARROK),
>>> + 100, 1000);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,
>>> + STM32_LPTIM_CMPOKCF_ARROKCF);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + ret = clk_enable(priv->clk);
>>> + if (ret) {
>>> + regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
>>> + return ret;
>>> + }
>>> +
>>> + /* Start LP timer in continuous mode */
>>> + return regmap_update_bits(priv->regmap, STM32_LPTIM_CR,
>>> + STM32_LPTIM_CNTSTRT, STM32_LPTIM_CNTSTRT);
>>> +}
>>> +
>>> +static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
>>> +{
>>> + u32 mask = STM32_LPTIM_ENC | STM32_LPTIM_COUNTMODE |
>>> + STM32_LPTIM_CKPOL | STM32_LPTIM_PRESC;
>>> + u32 val;
>>> +
>>> + /* Setup LP timer encoder/counter and polarity, without prescaler */
>>> + if (priv->quadrature_mode)
>>> + val = enable ? STM32_LPTIM_ENC : 0;
>>> + else
>>> + val = enable ? STM32_LPTIM_COUNTMODE : 0;
>>> + val |= FIELD_PREP(STM32_LPTIM_CKPOL, enable ? priv->polarity : 0);
>>> +
>>> + return regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask, val);
>>> +}
>>> +
>>> +static int stm32_lptim_write_raw(struct iio_dev *indio_dev,
>>> + struct iio_chan_spec const *chan,
>>> + int val, int val2, long mask)
>>> +{
>>> + struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>>> + int ret;
>>> +
>>> + switch (mask) {
>>> + case IIO_CHAN_INFO_ENABLE:
>>> + if (val < 0 || val > 1)
>>> + return -EINVAL;
>>> +
>>> + /* Check nobody uses the timer, or already disabled/enabled */
>>> + ret = stm32_lptim_is_enabled(priv);
>>> + if ((ret < 0) || (!ret && !val))
>>> + return ret;
>>> + if (val && ret)
>>> + return -EBUSY;
>>> +
>>> + ret = stm32_lptim_setup(priv, val);
>>> + if (ret)
>>> + return ret;
>>> + return stm32_lptim_set_enable_state(priv, val);
>>> +
>>> + default:
>>> + return -EINVAL;
>>> + }
>>> +}
>>> +
>>> +static int stm32_lptim_read_raw(struct iio_dev *indio_dev,
>>> + struct iio_chan_spec const *chan,
>>> + int *val, int *val2, long mask)
>>> +{
>>> + struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>>> + u32 dat;
>>> + int ret;
>>> +
>>> + switch (mask) {
>>> + case IIO_CHAN_INFO_RAW:
>>> + ret = regmap_read(priv->regmap, STM32_LPTIM_CNT, &dat);
>>> + if (ret)
>>> + return ret;
>>> + *val = dat;
>>> + return IIO_VAL_INT;
>>> +
>>> + case IIO_CHAN_INFO_ENABLE:
>>> + ret = stm32_lptim_is_enabled(priv);
>>> + if (ret < 0)
>>> + return ret;
>>> + *val = ret;
>>> + return IIO_VAL_INT;
>>> +
>>> + case IIO_CHAN_INFO_SCALE:
>>> + /* Non-quadrature mode: scale = 1 */
>>Both edges case?
>>> + *val = 1;
>>> + *val2 = 0;
>>> + if (priv->quadrature_mode) {
>>> + /*
>>> + * Quadrature encoder mode:
>>> + * - both edges, quarter cycle, scale is 0.25
>>> + * - either rising/falling edge scale is 0.5
>>> + */
>>> + if (priv->polarity > 1)
>>> + *val2 = 2;
>>> + else
>>> + *val2 = 1;
>>> + }
>>> + return IIO_VAL_FRACTIONAL_LOG2;
>>> +
>>> + default:
>>> + return -EINVAL;
>>> + }
>>> +}
>>> +
>>> +static const struct iio_info stm32_lptim_cnt_iio_info = {
>>> + .read_raw = stm32_lptim_read_raw,
>>> + .write_raw = stm32_lptim_write_raw,
>>> + .driver_module = THIS_MODULE,
>>> +};
>>> +
>>> +static const char *const stm32_lptim_quadrature_modes[] = {
>>> + "non-quadrature",
>>> + "quadrature",
>>> +};
>>> +
>>> +static int stm32_lptim_get_quadrature_mode(struct iio_dev *indio_dev,
>>> + const struct iio_chan_spec *chan)
>>> +{
>>> + struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>>> +
>>> + return priv->quadrature_mode;
>>> +}
>>> +
>>> +static int stm32_lptim_set_quadrature_mode(struct iio_dev *indio_dev,
>>> + const struct iio_chan_spec *chan,
>>> + unsigned int type)
>>> +{
>>> + struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>>> +
>>> + if (stm32_lptim_is_enabled(priv))
>>> + return -EBUSY;
>>> +
>>> + priv->quadrature_mode = type;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct iio_enum stm32_lptim_quadrature_mode_en = {
>>> + .items = stm32_lptim_quadrature_modes,
>>> + .num_items = ARRAY_SIZE(stm32_lptim_quadrature_modes),
>>> + .get = stm32_lptim_get_quadrature_mode,
>>> + .set = stm32_lptim_set_quadrature_mode,
>>> +};
>>> +
>>> +static const char * const stm32_lptim_cnt_polarity[] = {
>>> + "rising-edge", "falling-edge", "both-edges",
>>> +};
>>> +
>>> +static int stm32_lptim_cnt_get_polarity(struct iio_dev *indio_dev,
>>> + const struct iio_chan_spec *chan)
>>> +{
>>> + struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>>> +
>>> + return priv->polarity;
>>> +}
>>> +
>>> +static int stm32_lptim_cnt_set_polarity(struct iio_dev *indio_dev,
>>> + const struct iio_chan_spec *chan,
>>> + unsigned int type)
>>> +{
>>> + struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>>> +
>>> + if (stm32_lptim_is_enabled(priv))
>>> + return -EBUSY;
>>> +
>>> + priv->polarity = type;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct iio_enum stm32_lptim_cnt_polarity_en = {
>>> + .items = stm32_lptim_cnt_polarity,
>>> + .num_items = ARRAY_SIZE(stm32_lptim_cnt_polarity),
>>> + .get = stm32_lptim_cnt_get_polarity,
>>> + .set = stm32_lptim_cnt_set_polarity,
>>> +};
>>> +
>>> +static ssize_t stm32_lptim_cnt_get_preset(struct iio_dev *indio_dev,
>>> + uintptr_t private,
>>> + const struct iio_chan_spec *chan,
>>> + char *buf)
>>> +{
>>> + struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>>> +
>>> + return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset);
>>> +}
>>> +
>>> +static ssize_t stm32_lptim_cnt_set_preset(struct iio_dev *indio_dev,
>>> + uintptr_t private,
>>> + const struct iio_chan_spec *chan,
>>> + const char *buf, size_t len)
>>> +{
>>> + struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>>> + int ret;
>>> +
>>> + if (stm32_lptim_is_enabled(priv))
>>> + return -EBUSY;
>>> +
>>> + ret = kstrtouint(buf, 0, &priv->preset);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + if (priv->preset > STM32_LPTIM_MAX_ARR)
>>> + return -EINVAL;
>>> +
>>> + return len;
>>> +}
>>> +
>>> +/* LP timer with encoder */
>>> +static const struct iio_chan_spec_ext_info stm32_lptim_enc_ext_info[] = {
>>> + {
>>> + .name = "preset",
>>> + .shared = IIO_SEPARATE,
>>> + .read = stm32_lptim_cnt_get_preset,
>>> + .write = stm32_lptim_cnt_set_preset,
>>> + },
>>> + IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
>>> + IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
>>> + IIO_ENUM("quadrature_mode", IIO_SEPARATE,
>>> + &stm32_lptim_quadrature_mode_en),
>>> + IIO_ENUM_AVAILABLE("quadrature_mode", &stm32_lptim_quadrature_mode_en),
>>> + {}
>>> +};
>>> +
>>> +static const struct iio_chan_spec stm32_lptim_enc_channels = {
>>> + .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_lptim_enc_ext_info,
>>> + .indexed = 1,
>>> +};
>>> +
>>> +/* LP timer without encoder (counter only) */
>>> +static const struct iio_chan_spec_ext_info stm32_lptim_cnt_ext_info[] = {
>>> + {
>>> + .name = "preset",
>>> + .shared = IIO_SEPARATE,
>>> + .read = stm32_lptim_cnt_get_preset,
>>> + .write = stm32_lptim_cnt_set_preset,
>>> + },
>>> + IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
>>> + IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
>>> + {}
>>> +};
>>> +
>>> +static const struct iio_chan_spec stm32_lptim_cnt_channels = {
>>> + .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_lptim_cnt_ext_info,
>>> + .indexed = 1,
>>> +};
>>> +
>>> +static int stm32_lptim_cnt_probe(struct platform_device *pdev)
>>> +{
>>> + struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
>>> + struct stm32_lptim_cnt *priv;
>>> + struct iio_dev *indio_dev;
>>> +
>>> + if (IS_ERR_OR_NULL(ddata))
>>> + return -EINVAL;
>>> +
>>> + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
>>> + if (!indio_dev)
>>> + return -ENOMEM;
>>> +
>>> + priv = iio_priv(indio_dev);
>>> + priv->dev = &pdev->dev;
>>> + priv->regmap = ddata->regmap;
>>> + priv->clk = ddata->clk;
>>> + priv->preset = STM32_LPTIM_MAX_ARR;
>>> +
>>> + indio_dev->name = dev_name(&pdev->dev);
>>> + indio_dev->dev.parent = &pdev->dev;
>>> + indio_dev->dev.of_node = pdev->dev.of_node;
>>> + indio_dev->info = &stm32_lptim_cnt_iio_info;
>>> + if (ddata->has_encoder)
>>> + indio_dev->channels = &stm32_lptim_enc_channels;
>>> + else
>>> + indio_dev->channels = &stm32_lptim_cnt_channels;
>>> + indio_dev->num_channels = 1;
>>> +
>>> + platform_set_drvdata(pdev, priv);
>>> +
>>> + return devm_iio_device_register(&pdev->dev, indio_dev);
>>> +}
>>> +
>>> +static const struct of_device_id stm32_lptim_cnt_of_match[] = {
>>> + { .compatible = "st,stm32-lptimer-counter", },
>>> + {},
>>> +};
>>> +MODULE_DEVICE_TABLE(of, stm32_lptim_cnt_of_match);
>>> +
>>> +static struct platform_driver stm32_lptim_cnt_driver = {
>>> + .probe = stm32_lptim_cnt_probe,
>>> + .driver = {
>>> + .name = "stm32-lptimer-counter",
>>> + .of_match_table = stm32_lptim_cnt_of_match,
>>> + },
>>> +};
>>> +module_platform_driver(stm32_lptim_cnt_driver);
>>> +
>>> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@xxxxxx>");
>>> +MODULE_ALIAS("platform:stm32-lptimer-counter");
>>> +MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
>>> +MODULE_LICENSE("GPL v2");
>>
--
Benjamin Gaignard
Graphic Study Group
Linaro.org â Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog