2017-06-21 14:31:22

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH v2 0/8] Add STM32 LPTimer: PWM, trigger and counter

This series adds support for Low-Power Timer that can be found on
some STM32 devices.

STM32 LPTimer (LPTIM) is a 16-bit timer that provides several
functionalities. This series adds support for following features:
- PWM output (with programmable prescaler, configurable polarity)
- Trigger source for STM32 ADC or DAC (LPTIM_OUT)
- Quadrature encoder and counter

The MFD core is used to manage common resources (clock, register map)
and to detect encoder feature. "stm32_lptimer" structure is provided
to its sub-nodes to share those information:
- PWM driver is used to implement single PWM channel
- IIO trigger
- IIO quadrature encoder and counter

---
Changes in v2:
- Various remarks from Lee, on MFD part, extended to the full series,
such as: clock name, use "Low-Power Timer", file headers, dt-bindings
props descriptions, fix dt example.

Fabrice Gasnier (8):
dt-bindings: mfd: Add STM32 LPTimer binding
mfd: Add STM32 LPTimer driver
dt-bindings: pwm: Add STM32 LPTimer PWM binding
pwm: Add STM32 LPTimer PWM driver
dt-bindings: iio: Add STM32 LPTimer trigger binding
iio: trigger: Add STM32 LPTimer trigger driver
dt-bindings: iio: Add STM32 LPTimer quadrature encoder and counter
iio: counter: Add support for STM32 LPTimer

.../ABI/testing/sysfs-bus-iio-lptimer-stm32 | 57 +++
.../bindings/iio/counter/stm32-lptimer-cnt.txt | 27 ++
.../bindings/iio/timer/stm32-lptimer-trigger.txt | 23 ++
.../devicetree/bindings/mfd/stm32-lptimer.txt | 48 +++
.../devicetree/bindings/pwm/pwm-stm32-lp.txt | 24 ++
drivers/iio/counter/Kconfig | 9 +
drivers/iio/counter/Makefile | 1 +
drivers/iio/counter/stm32-lptimer-cnt.c | 383 +++++++++++++++++++++
drivers/iio/trigger/Kconfig | 11 +
drivers/iio/trigger/Makefile | 1 +
drivers/iio/trigger/stm32-lptimer-trigger.c | 110 ++++++
drivers/mfd/Kconfig | 14 +
drivers/mfd/Makefile | 1 +
drivers/mfd/stm32-lptimer.c | 107 ++++++
drivers/pwm/Kconfig | 10 +
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-stm32-lp.c | 216 ++++++++++++
include/linux/iio/timer/stm32-lptim-trigger.h | 24 ++
include/linux/mfd/stm32-lptimer.h | 62 ++++
19 files changed, 1129 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
create mode 100644 Documentation/devicetree/bindings/iio/counter/stm32-lptimer-cnt.txt
create mode 100644 Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt
create mode 100644 Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
create mode 100644 Documentation/devicetree/bindings/pwm/pwm-stm32-lp.txt
create mode 100644 drivers/iio/counter/stm32-lptimer-cnt.c
create mode 100644 drivers/iio/trigger/stm32-lptimer-trigger.c
create mode 100644 drivers/mfd/stm32-lptimer.c
create mode 100644 drivers/pwm/pwm-stm32-lp.c
create mode 100644 include/linux/iio/timer/stm32-lptim-trigger.h
create mode 100644 include/linux/mfd/stm32-lptimer.h

--
1.9.1


2017-06-21 14:31:25

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH v2 5/8] dt-bindings: iio: Add STM32 LPTimer trigger binding

Add documentation for STMicroelectronics STM32 Low-Power Timer Trigger
binding.

Signed-off-by: Fabrice Gasnier <[email protected]>
---
Changes in v2:
- s/Low Power/Low-Power
- remove leading 0x in example (parent node)
- improve reg property description
---
.../bindings/iio/timer/stm32-lptimer-trigger.txt | 23 ++++++++++++++++++++++
1 file changed, 23 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt

diff --git a/Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt b/Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt
new file mode 100644
index 0000000..466d99f
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt
@@ -0,0 +1,23 @@
+STMicroelectronics STM32 Low-Power Timer Trigger
+
+STM32 Low-Power Timer provides trigger source (LPTIM output) that can be used
+by STM32 internal ADC and/or DAC.
+
+Must be a sub-node of an STM32 Low-Power Timer device tree node.
+See ../mfd/stm32-lptimer.txt for details about the parent node.
+
+Required properties:
+- compatible: Must be "st,stm32-lptimer-trigger".
+- reg: Selects trigger hardware block. Must be 0, 1 or 2
+ respectively for lptimer1, lptimer2 or lptimer3
+ trigger output.
+
+Example:
+ lptimer1: lptimer@40002400 {
+ compatible = "st,stm32-lptimer";
+ ...
+ trigger@0 {
+ compatible = "st,stm32-lptimer-trigger";
+ reg = <0>;
+ };
+ };
--
1.9.1

2017-06-21 14:31:32

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH v2 8/8] iio: counter: Add support for STM32 LPTimer

Add support for STM32 Low-Power Timer, that can be used as counter
or quadrature encoder.

Signed-off-by: Fabrice Gasnier <[email protected]>
---
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: [email protected]
+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).
+
+What: /sys/bus/iio/devices/iio:deviceX/in_count_quadrature_mode_available
+KernelVersion: 4.13
+Contact: [email protected]
+Description:
+ Reading returns the list possible quadrature modes.
+
+What: /sys/bus/iio/devices/iio:deviceX/in_count0_quadrature_mode
+KernelVersion: 4.13
+Contact: [email protected]
+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.
+
+What: /sys/bus/iio/devices/iio:deviceX/in_count_polarity_available
+KernelVersion: 4.13
+Contact: [email protected]
+Description:
+ Reading returns the list possible active edges.
+
+What: /sys/bus/iio/devices/iio:deviceX/in_count0_polarity
+KernelVersion: 4.13
+Contact: [email protected]
+Description:
+ Configure the device encoder/counter active edge:
+ - rising-edge
+ - falling-edge
+ - both-edges
+
+ 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 |
+ ----------------------------------------------------------------
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 <[email protected]>
+ *
+ * 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 */
+ *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 <[email protected]>");
+MODULE_ALIAS("platform:stm32-lptimer-counter");
+MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
+MODULE_LICENSE("GPL v2");
--
1.9.1

2017-06-21 14:31:23

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH v2 3/8] dt-bindings: pwm: Add STM32 LPTimer PWM binding

Add documentation for STMicroelectronics STM32 Low-Power Timer
PWM binding.

Signed-off-by: Fabrice Gasnier <[email protected]>
---
Changes in v2:
- s/Low Power/Low-Power/
- remove 0x in example (parent node)
- improve properties description
---
.../devicetree/bindings/pwm/pwm-stm32-lp.txt | 24 ++++++++++++++++++++++
1 file changed, 24 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pwm/pwm-stm32-lp.txt

diff --git a/Documentation/devicetree/bindings/pwm/pwm-stm32-lp.txt b/Documentation/devicetree/bindings/pwm/pwm-stm32-lp.txt
new file mode 100644
index 0000000..941c990
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/pwm-stm32-lp.txt
@@ -0,0 +1,24 @@
+STMicroelectronics STM32 Low-Power Timer PWM
+
+STM32 Low-Power Timer provides single channel PWM.
+
+Must be a sub-node of an STM32 Low-Power Timer device tree node.
+See ../mfd/stm32-lptimer.txt for details about the parent node.
+
+Required parameters:
+- compatible: Must be "st,stm32-pwm-lp".
+
+Optional properties:
+- pinctrl-names: Set to "default".
+- pinctrl-0: Phandle pointing to pin configuration node for PWM.
+
+Example:
+ lptimer1: lptimer@40002400 {
+ compatible = "st,stm32-lptimer";
+ ...
+ pwm {
+ compatible = "st,stm32-pwm-lp";
+ pinctrl-names = "default";
+ pinctrl-0 = <&lppwm1_pins>;
+ };
+ };
--
1.9.1

2017-06-21 14:31:19

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH v2 4/8] pwm: Add STM32 LPTimer PWM driver

Add support for single PWM channel on Low-Power Timer, that can be
found on some STM32 platforms.

Signed-off-by: Fabrice Gasnier <[email protected]>
---
Changes in v2:
- s/Low Power/Low-Power
- update few comment lines
---
drivers/pwm/Kconfig | 10 +++
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-stm32-lp.c | 216 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 227 insertions(+)
create mode 100644 drivers/pwm/pwm-stm32-lp.c

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 313c107..7cb982b 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -417,6 +417,16 @@ config PWM_STM32
To compile this driver as a module, choose M here: the module
will be called pwm-stm32.

+config PWM_STM32_LP
+ tristate "STMicroelectronics STM32 PWM LP"
+ depends on MFD_STM32_LPTIMER || COMPILE_TEST
+ help
+ Generic PWM framework driver for STMicroelectronics STM32 SoCs
+ with Low-Power Timer (LPTIM).
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-stm32-lp.
+
config PWM_STMPE
bool "STMPE expander PWM export"
depends on MFD_STMPE
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 93da1f7..a3a4bee 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o
obj-$(CONFIG_PWM_STI) += pwm-sti.o
obj-$(CONFIG_PWM_STM32) += pwm-stm32.o
+obj-$(CONFIG_PWM_STM32_LP) += pwm-stm32-lp.o
obj-$(CONFIG_PWM_STMPE) += pwm-stmpe.o
obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o
obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o
diff --git a/drivers/pwm/pwm-stm32-lp.c b/drivers/pwm/pwm-stm32-lp.c
new file mode 100644
index 0000000..eb997a8
--- /dev/null
+++ b/drivers/pwm/pwm-stm32-lp.c
@@ -0,0 +1,216 @@
+/*
+ * STM32 Low-Power Timer PWM driver
+ *
+ * Copyright (C) STMicroelectronics 2017
+ *
+ * Author: Gerald Baeza <[email protected]>
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * Inspired by Gerald Baeza's pwm-stm32 driver
+ */
+
+#include <linux/bitfield.h>
+#include <linux/mfd/stm32-lptimer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+struct stm32_pwm_lp {
+ struct pwm_chip chip;
+ struct clk *clk;
+ struct regmap *regmap;
+};
+
+static inline struct stm32_pwm_lp *to_stm32_pwm_lp(struct pwm_chip *chip)
+{
+ return container_of(chip, struct stm32_pwm_lp, chip);
+}
+
+static const u8 prescalers[] = {1, 2, 4, 8, 16, 32, 64, 128};
+
+static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct stm32_pwm_lp *priv = to_stm32_pwm_lp(chip);
+ unsigned long long prd, div, dty;
+ struct pwm_state cstate;
+ u32 val, mask, cfgr, wavpol, presc = 0;
+ bool reenable = false;
+ int ret;
+
+ pwm_get_state(pwm, &cstate);
+
+ if (!state->enabled) {
+ if (cstate.enabled) {
+ /* Disable LP timer */
+ ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
+ if (ret)
+ return ret;
+ clk_disable(priv->clk);
+ }
+ return 0;
+ }
+
+ /* Calculate the period and prescaler value */
+ div = (unsigned long long)clk_get_rate(priv->clk) * state->period;
+ do_div(div, NSEC_PER_SEC);
+ prd = div;
+ while (div > STM32_LPTIM_MAX_ARR) {
+ presc++;
+ if (presc >= ARRAY_SIZE(prescalers)) {
+ dev_err(priv->chip.dev, "max prescaler exceeded\n");
+ return -EINVAL;
+ }
+ div = prd;
+ do_div(div, prescalers[presc]);
+ }
+ prd = div;
+
+ /* Calculate the duty cycle */
+ dty = prd * state->duty_cycle;
+ do_div(dty, state->period);
+
+ wavpol = FIELD_PREP(STM32_LPTIM_WAVPOL, state->polarity);
+
+ if (!cstate.enabled) {
+ ret = clk_enable(priv->clk);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_read(priv->regmap, STM32_LPTIM_CFGR, &cfgr);
+ if (ret)
+ goto err;
+
+ if ((wavpol != FIELD_GET(STM32_LPTIM_WAVPOL, cfgr)) ||
+ (presc != FIELD_GET(STM32_LPTIM_PRESC, cfgr))) {
+ val = FIELD_PREP(STM32_LPTIM_PRESC, presc) | wavpol;
+ mask = STM32_LPTIM_PRESC | STM32_LPTIM_WAVPOL;
+
+ /* Must disable LP timer to modify CFGR */
+ ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
+ if (ret)
+ goto err;
+ reenable = true;
+ ret = regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask,
+ val);
+ if (ret)
+ goto err;
+ }
+
+ if (!cstate.enabled || reenable) {
+ /* Must enable LP timer to modify CMP & ARR */
+ ret = regmap_write(priv->regmap, STM32_LPTIM_CR,
+ STM32_LPTIM_ENABLE);
+ if (ret)
+ goto err;
+ }
+
+ ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, prd - 1);
+ if (ret)
+ goto err;
+
+ ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, prd - (1 + dty));
+ if (ret)
+ goto err;
+
+ /* 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) {
+ dev_err(priv->chip.dev, "ARR/CMP registers write issue\n");
+ goto err;
+ }
+ ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,
+ STM32_LPTIM_CMPOKCF_ARROKCF);
+ if (ret)
+ goto err;
+
+ if (!cstate.enabled || reenable) {
+ /* Start LP timer in continuous mode */
+ ret = regmap_update_bits(priv->regmap, STM32_LPTIM_CR,
+ STM32_LPTIM_CNTSTRT,
+ STM32_LPTIM_CNTSTRT);
+ if (ret) {
+ regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
+ goto err;
+ }
+ }
+
+ return 0;
+err:
+ if (!cstate.enabled)
+ clk_disable(priv->clk);
+
+ return ret;
+}
+
+static const struct pwm_ops stm32_pwm_lp_ops = {
+ .owner = THIS_MODULE,
+ .apply = stm32_pwm_lp_apply,
+};
+
+static int stm32_pwm_lp_probe(struct platform_device *pdev)
+{
+ struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
+ struct stm32_pwm_lp *priv;
+ int ret;
+
+ if (IS_ERR_OR_NULL(ddata))
+ return -EINVAL;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->regmap = ddata->regmap;
+ priv->clk = ddata->clk;
+ if (!priv->regmap || !priv->clk)
+ return -EINVAL;
+
+ priv->chip.base = -1;
+ priv->chip.dev = &pdev->dev;
+ priv->chip.ops = &stm32_pwm_lp_ops;
+ priv->chip.npwm = 1;
+
+ ret = pwmchip_add(&priv->chip);
+ if (ret < 0)
+ return ret;
+
+ platform_set_drvdata(pdev, priv);
+
+ return 0;
+}
+
+static int stm32_pwm_lp_remove(struct platform_device *pdev)
+{
+ struct stm32_pwm_lp *priv = platform_get_drvdata(pdev);
+
+ if (pwm_is_enabled(priv->chip.pwms))
+ pwm_disable(priv->chip.pwms);
+
+ return pwmchip_remove(&priv->chip);
+}
+
+static const struct of_device_id stm32_pwm_lp_of_match[] = {
+ { .compatible = "st,stm32-pwm-lp", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32_pwm_lp_of_match);
+
+static struct platform_driver stm32_pwm_lp_driver = {
+ .probe = stm32_pwm_lp_probe,
+ .remove = stm32_pwm_lp_remove,
+ .driver = {
+ .name = "stm32-pwm-lp",
+ .of_match_table = of_match_ptr(stm32_pwm_lp_of_match),
+ },
+};
+module_platform_driver(stm32_pwm_lp_driver);
+
+MODULE_ALIAS("platform:stm32-pwm-lp");
+MODULE_DESCRIPTION("STMicroelectronics STM32 PWM LP driver");
+MODULE_LICENSE("GPL v2");
--
1.9.1

2017-06-21 14:32:39

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH v2 6/8] iio: trigger: Add STM32 LPTimer trigger driver

Add support for LPTIMx_OUT triggers that can be found on some STM32
devices. These triggers can be used then by ADC or DAC.
Typical usage is to configure LPTimer as PWM output (via pwm-stm32-lp)
and have synchronised analog conversions with these triggers.

Signed-off-by: Fabrice Gasnier <[email protected]>
---
Changes in v2:
- s/Low Power/Low-Power
- update few comments
---
drivers/iio/trigger/Kconfig | 11 +++
drivers/iio/trigger/Makefile | 1 +
drivers/iio/trigger/stm32-lptimer-trigger.c | 110 ++++++++++++++++++++++++++
include/linux/iio/timer/stm32-lptim-trigger.h | 24 ++++++
4 files changed, 146 insertions(+)
create mode 100644 drivers/iio/trigger/stm32-lptimer-trigger.c
create mode 100644 include/linux/iio/timer/stm32-lptim-trigger.h

diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig
index e4d4e63..a633d2c 100644
--- a/drivers/iio/trigger/Kconfig
+++ b/drivers/iio/trigger/Kconfig
@@ -24,6 +24,17 @@ config IIO_INTERRUPT_TRIGGER
To compile this driver as a module, choose M here: the
module will be called iio-trig-interrupt.

+config IIO_STM32_LPTIMER_TRIGGER
+ tristate "STM32 Low-Power Timer Trigger"
+ depends on MFD_STM32_LPTIMER || COMPILE_TEST
+ help
+ Select this option to enable STM32 Low-Power Timer Trigger.
+ This can be used as trigger source for STM32 internal ADC
+ and/or DAC.
+
+ To compile this driver as a module, choose M here: the
+ module will be called stm32-lptimer-trigger.
+
config IIO_STM32_TIMER_TRIGGER
tristate "STM32 Timer Trigger"
depends on (ARCH_STM32 && OF && MFD_STM32_TIMERS) || COMPILE_TEST
diff --git a/drivers/iio/trigger/Makefile b/drivers/iio/trigger/Makefile
index 5c4ecd3..0a72a2a 100644
--- a/drivers/iio/trigger/Makefile
+++ b/drivers/iio/trigger/Makefile
@@ -6,6 +6,7 @@

obj-$(CONFIG_IIO_HRTIMER_TRIGGER) += iio-trig-hrtimer.o
obj-$(CONFIG_IIO_INTERRUPT_TRIGGER) += iio-trig-interrupt.o
+obj-$(CONFIG_IIO_STM32_LPTIMER_TRIGGER) += stm32-lptimer-trigger.o
obj-$(CONFIG_IIO_STM32_TIMER_TRIGGER) += stm32-timer-trigger.o
obj-$(CONFIG_IIO_SYSFS_TRIGGER) += iio-trig-sysfs.o
obj-$(CONFIG_IIO_TIGHTLOOP_TRIGGER) += iio-trig-loop.o
diff --git a/drivers/iio/trigger/stm32-lptimer-trigger.c b/drivers/iio/trigger/stm32-lptimer-trigger.c
new file mode 100644
index 0000000..bcb9aa2
--- /dev/null
+++ b/drivers/iio/trigger/stm32-lptimer-trigger.c
@@ -0,0 +1,110 @@
+/*
+ * STM32 Low-Power Timer Trigger driver
+ *
+ * Copyright (C) STMicroelectronics 2017
+ *
+ * Author: Fabrice Gasnier <[email protected]>.
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * Inspired by Benjamin Gaignard's stm32-timer-trigger driver
+ */
+
+#include <linux/iio/iio.h>
+#include <linux/iio/timer/stm32-lptim-trigger.h>
+#include <linux/iio/trigger.h>
+#include <linux/mfd/stm32-lptimer.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+/* List Low-Power Timer triggers */
+static const char * const stm32_lptim_triggers[] = {
+ LPTIM1_OUT,
+ LPTIM2_OUT,
+ LPTIM3_OUT,
+};
+
+struct stm32_lptim_trigger {
+ struct device *dev;
+ const char *trg;
+};
+
+static const struct iio_trigger_ops stm32_lptim_trigger_ops = {
+ .owner = THIS_MODULE,
+};
+
+/**
+ * is_stm32_lptim_trigger
+ * @trig: trigger to be checked
+ *
+ * return true if the trigger is a valid STM32 IIO Low-Power Timer Trigger
+ * either return false
+ */
+bool is_stm32_lptim_trigger(struct iio_trigger *trig)
+{
+ return (trig->ops == &stm32_lptim_trigger_ops);
+}
+EXPORT_SYMBOL(is_stm32_lptim_trigger);
+
+static int stm32_lptim_setup_trig(struct stm32_lptim_trigger *priv)
+{
+ struct iio_trigger *trig;
+
+ trig = devm_iio_trigger_alloc(priv->dev, "%s", priv->trg);
+ if (!trig)
+ return -ENOMEM;
+
+ trig->dev.parent = priv->dev->parent;
+ trig->ops = &stm32_lptim_trigger_ops;
+ iio_trigger_set_drvdata(trig, priv);
+
+ return devm_iio_trigger_register(priv->dev, trig);
+}
+
+static int stm32_lptim_trigger_probe(struct platform_device *pdev)
+{
+ struct stm32_lptim_trigger *priv;
+ u32 index;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ if (of_property_read_u32(pdev->dev.of_node, "reg", &index))
+ return -EINVAL;
+
+ if (index >= ARRAY_SIZE(stm32_lptim_triggers))
+ return -EINVAL;
+
+ priv->dev = &pdev->dev;
+ priv->trg = stm32_lptim_triggers[index];
+
+ ret = stm32_lptim_setup_trig(priv);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, priv);
+
+ return 0;
+}
+
+static const struct of_device_id stm32_lptim_trig_of_match[] = {
+ { .compatible = "st,stm32-lptimer-trigger", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32_lptim_trig_of_match);
+
+static struct platform_driver stm32_lptim_trigger_driver = {
+ .probe = stm32_lptim_trigger_probe,
+ .driver = {
+ .name = "stm32-lptimer-trigger",
+ .of_match_table = stm32_lptim_trig_of_match,
+ },
+};
+module_platform_driver(stm32_lptim_trigger_driver);
+
+MODULE_AUTHOR("Fabrice Gasnier <[email protected]>");
+MODULE_ALIAS("platform:stm32-lptimer-trigger");
+MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM trigger driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/iio/timer/stm32-lptim-trigger.h b/include/linux/iio/timer/stm32-lptim-trigger.h
new file mode 100644
index 0000000..cb795b1
--- /dev/null
+++ b/include/linux/iio/timer/stm32-lptim-trigger.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) STMicroelectronics 2017
+ *
+ * Author: Fabrice Gasnier <[email protected]>
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STM32_LPTIM_TRIGGER_H_
+#define _STM32_LPTIM_TRIGGER_H_
+
+#define LPTIM1_OUT "lptim1_out"
+#define LPTIM2_OUT "lptim2_out"
+#define LPTIM3_OUT "lptim3_out"
+
+#if IS_ENABLED(CONFIG_IIO_STM32_LPTIMER_TRIGGER)
+bool is_stm32_lptim_trigger(struct iio_trigger *trig);
+#else
+static inline bool is_stm32_lptim_trigger(struct iio_trigger *trig)
+{
+ return false;
+}
+#endif
+#endif
--
1.9.1

2017-06-21 14:35:13

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH v2 2/8] mfd: Add STM32 LPTimer driver

STM32 Low-Power Timer hardware block can be used for:
- PWM generation
- IIO trigger (in sync with PWM)
- IIO quadrature encoder counter
PWM and IIO timer configuration are mixed in the same registers so
we need a multi fonction driver to be able to share those registers.

Signed-off-by: Fabrice Gasnier <[email protected]>
---
Changes in v2:
- Lee's remarks: various comments, max register define, s/Low Power/Low-Power,
clock name, removed reset, add kernel doc for stm32_lptimer struct
---
drivers/mfd/Kconfig | 14 +++++
drivers/mfd/Makefile | 1 +
drivers/mfd/stm32-lptimer.c | 107 ++++++++++++++++++++++++++++++++++++++
include/linux/mfd/stm32-lptimer.h | 62 ++++++++++++++++++++++
4 files changed, 184 insertions(+)
create mode 100644 drivers/mfd/stm32-lptimer.c
create mode 100644 include/linux/mfd/stm32-lptimer.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 3eb5c93..8e1ca44 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1683,6 +1683,20 @@ config MFD_STW481X
in various ST Microelectronics and ST-Ericsson embedded
Nomadik series.

+config MFD_STM32_LPTIMER
+ tristate "Support for STM32 Low-Power Timer"
+ depends on (ARCH_STM32 && OF) || COMPILE_TEST
+ select MFD_CORE
+ select REGMAP
+ select REGMAP_MMIO
+ help
+ Select this option to enable STM32 Low-Power Timer driver
+ used for PWM, IIO Trigger, IIO Encoder and Counter. Shared
+ resources are also dealt with here.
+
+ To compile this driver as a module, choose M here: the
+ module will be called stm32-lptimer.
+
config MFD_STM32_TIMERS
tristate "Support for STM32 Timers"
depends on (ARCH_STM32 && OF) || COMPILE_TEST
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index c16bf1e..a5308d8 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -219,5 +219,6 @@ obj-$(CONFIG_MFD_MT6397) += mt6397-core.o
obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o
obj-$(CONFIG_MFD_SUN4I_GPADC) += sun4i-gpadc.o

+obj-$(CONFIG_MFD_STM32_LPTIMER) += stm32-lptimer.o
obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o
obj-$(CONFIG_MFD_MXS_LRADC) += mxs-lradc.o
diff --git a/drivers/mfd/stm32-lptimer.c b/drivers/mfd/stm32-lptimer.c
new file mode 100644
index 0000000..075330a
--- /dev/null
+++ b/drivers/mfd/stm32-lptimer.c
@@ -0,0 +1,107 @@
+/*
+ * STM32 Low-Power Timer parent driver.
+ *
+ * Copyright (C) STMicroelectronics 2017
+ *
+ * Author: Fabrice Gasnier <[email protected]>
+ *
+ * Inspired by Benjamin Gaignard's stm32-timers driver
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/mfd/stm32-lptimer.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+
+#define STM32_LPTIM_MAX_REGISTER 0x3fc
+
+static const struct regmap_config stm32_lptimer_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = sizeof(u32),
+ .max_register = STM32_LPTIM_MAX_REGISTER,
+};
+
+static int stm32_lptimer_detect_encoder(struct stm32_lptimer *ddata)
+{
+ u32 val;
+ int ret;
+
+ /*
+ * Quadrature encoder mode bit can only be written and read back when
+ * Low-Power Timer supports it.
+ */
+ ret = regmap_update_bits(ddata->regmap, STM32_LPTIM_CFGR,
+ STM32_LPTIM_ENC, STM32_LPTIM_ENC);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(ddata->regmap, STM32_LPTIM_CFGR, &val);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(ddata->regmap, STM32_LPTIM_CFGR,
+ STM32_LPTIM_ENC, 0);
+ if (ret)
+ return ret;
+
+ ddata->has_encoder = !!(val & STM32_LPTIM_ENC);
+
+ return 0;
+}
+
+static int stm32_lptimer_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct stm32_lptimer *ddata;
+ struct resource *res;
+ void __iomem *mmio;
+ int ret;
+
+ ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
+ if (!ddata)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ mmio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(mmio))
+ return PTR_ERR(mmio);
+
+ ddata->regmap = devm_regmap_init_mmio_clk(dev, "mux", mmio,
+ &stm32_lptimer_regmap_cfg);
+ if (IS_ERR(ddata->regmap))
+ return PTR_ERR(ddata->regmap);
+
+ ddata->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(ddata->clk))
+ return PTR_ERR(ddata->clk);
+
+ ret = stm32_lptimer_detect_encoder(ddata);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, ddata);
+
+ return devm_of_platform_populate(&pdev->dev);
+}
+
+static const struct of_device_id stm32_lptimer_of_match[] = {
+ { .compatible = "st,stm32-lptimer", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32_lptimer_of_match);
+
+static struct platform_driver stm32_lptimer_driver = {
+ .probe = stm32_lptimer_probe,
+ .driver = {
+ .name = "stm32-lptimer",
+ .of_match_table = stm32_lptimer_of_match,
+ },
+};
+module_platform_driver(stm32_lptimer_driver);
+
+MODULE_AUTHOR("Fabrice Gasnier <[email protected]>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 Low-Power Timer");
+MODULE_ALIAS("platform:stm32-lptimer");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/stm32-lptimer.h b/include/linux/mfd/stm32-lptimer.h
new file mode 100644
index 0000000..77c7cf4
--- /dev/null
+++ b/include/linux/mfd/stm32-lptimer.h
@@ -0,0 +1,62 @@
+/*
+ * STM32 Low-Power Timer parent driver.
+ *
+ * Copyright (C) STMicroelectronics 2017
+ *
+ * Author: Fabrice Gasnier <[email protected]>
+ *
+ * Inspired by Benjamin Gaignard's stm32-timers driver
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#ifndef _LINUX_STM32_LPTIMER_H_
+#define _LINUX_STM32_LPTIMER_H_
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+#define STM32_LPTIM_ISR 0x00 /* Interrupt and Status Reg */
+#define STM32_LPTIM_ICR 0x04 /* Interrupt Clear Reg */
+#define STM32_LPTIM_IER 0x08 /* Interrupt Enable Reg */
+#define STM32_LPTIM_CFGR 0x0C /* Configuration Reg */
+#define STM32_LPTIM_CR 0x10 /* Control Reg */
+#define STM32_LPTIM_CMP 0x14 /* Compare Reg */
+#define STM32_LPTIM_ARR 0x18 /* Autoreload Reg */
+#define STM32_LPTIM_CNT 0x1C /* Counter Reg */
+
+/* STM32_LPTIM_ISR - bit fields */
+#define STM32_LPTIM_CMPOK_ARROK GENMASK(4, 3)
+#define STM32_LPTIM_ARROK BIT(4)
+#define STM32_LPTIM_CMPOK BIT(3)
+
+/* STM32_LPTIM_ICR - bit fields */
+#define STM32_LPTIM_CMPOKCF_ARROKCF GENMASK(4, 3)
+
+/* STM32_LPTIM_CR - bit fields */
+#define STM32_LPTIM_CNTSTRT BIT(2)
+#define STM32_LPTIM_ENABLE BIT(0)
+
+/* STM32_LPTIM_CFGR - bit fields */
+#define STM32_LPTIM_ENC BIT(24)
+#define STM32_LPTIM_COUNTMODE BIT(23)
+#define STM32_LPTIM_WAVPOL BIT(21)
+#define STM32_LPTIM_PRESC GENMASK(11, 9)
+#define STM32_LPTIM_CKPOL GENMASK(2, 1)
+
+/* STM32_LPTIM_ARR */
+#define STM32_LPTIM_MAX_ARR 0xFFFF
+
+/**
+ * struct stm32_lptimer - STM32 Low-Power Timer data assigned by parent device
+ * @clk: clock reference for this instance
+ * @regmap: register map reference for this instance
+ * @has_encoder: indicates this Low-Power Timer supports encoder mode
+ */
+struct stm32_lptimer {
+ struct clk *clk;
+ struct regmap *regmap;
+ bool has_encoder;
+};
+
+#endif
--
1.9.1

2017-06-21 14:31:17

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH v2 1/8] dt-bindings: mfd: Add STM32 LPTimer binding

Add documentation for STMicroelectronics STM32 Low-Power Timer binding.

Signed-off-by: Fabrice Gasnier <[email protected]>
---
Changes in v2:
- Lee's comments: s/Low Power/Low-Power/, remove 0x in example, improve
properties descriptions
---
.../devicetree/bindings/mfd/stm32-lptimer.txt | 48 ++++++++++++++++++++++
1 file changed, 48 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/stm32-lptimer.txt

diff --git a/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt b/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
new file mode 100644
index 0000000..af859c8
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
@@ -0,0 +1,48 @@
+STMicroelectronics STM32 Low-Power Timer
+
+The STM32 Low-Power Timer (LPTIM) is a 16-bit timer that provides several
+functions:
+- PWM output (with programmable prescaler, configurable polarity)
+- Quadrature encoder, counter
+- Trigger source for STM32 ADC/DAC (LPTIM_OUT)
+
+Required properties:
+- compatible: Must be "st,stm32-lptimer".
+- reg: Offset and length of the device's register set.
+- clocks: Phandle to the clock used by the LP Timer module.
+- clock-names: Must be "mux".
+- #address-cells: Should be '<1>'.
+- #size-cells: Should be '<0>'.
+
+Optional subnodes:
+- pwm: See ../pwm/pwm-stm32-lp.txt
+- counter: See ../iio/timer/stm32-lptimer-cnt.txt
+- trigger: See ../iio/timer/stm32-lptimer-trigger.txt
+
+Example:
+
+ lptimer1: lptimer@40002400 {
+ compatible = "st,stm32-lptimer";
+ reg = <0x40002400 0x400>;
+ clocks = <&timer_clk>;
+ clock-names = "mux";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pwm {
+ compatible = "st,stm32-pwm-lp";
+ pinctrl-names = "default";
+ pinctrl-0 = <&lppwm1_pins>;
+ };
+
+ trigger@0 {
+ compatible = "st,stm32-lptimer-trigger";
+ reg = <0>;
+ };
+
+ counter {
+ compatible = "st,stm32-lptimer-counter";
+ pinctrl-names = "default";
+ pinctrl-0 = <&lptim1_in_pins>;
+ };
+ };
--
1.9.1

2017-06-21 14:36:07

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH v2 7/8] dt-bindings: iio: Add STM32 LPTimer quadrature encoder and counter

Add documentation for STMicroelectronics STM32 Low-Power Timer
quadrature encoder and counter binding.

Signed-off-by: Fabrice Gasnier <[email protected]>
---
Changes in v2:
- s/Low Power/Low-Power
- remove leading 0x in example (parent node)
- improve properties descriptions
---
.../bindings/iio/counter/stm32-lptimer-cnt.txt | 27 ++++++++++++++++++++++
1 file changed, 27 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/counter/stm32-lptimer-cnt.txt

diff --git a/Documentation/devicetree/bindings/iio/counter/stm32-lptimer-cnt.txt b/Documentation/devicetree/bindings/iio/counter/stm32-lptimer-cnt.txt
new file mode 100644
index 0000000..b9fd014
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/counter/stm32-lptimer-cnt.txt
@@ -0,0 +1,27 @@
+STMicroelectronics STM32 Low-Power Timer quadrature encoder and counter
+
+STM32 Low-Power Timer provides several counter modes. It can be used as:
+- quadrature encoder to detect angular position and direction of rotary
+ elements, from IN1 and IN2 input signals.
+- simple counter from IN1 input signal.
+
+Must be a sub-node of an STM32 Low-Power Timer device tree node.
+See ../mfd/stm32-lptimer.txt for details about the parent node.
+
+Required properties:
+- compatible: Must be "st,stm32-lptimer-counter".
+- pinctrl-names: Set to "default".
+- pinctrl-0: List of phandles pointing to pin configuration nodes,
+ to set IN1/IN2 pins in mode of operation for Low-Power
+ Timer input on external pin.
+
+Example:
+ lptimer1: lptimer@40002400 {
+ compatible = "st,stm32-lptimer";
+ ...
+ counter {
+ compatible = "st,stm32-lptimer-counter";
+ pinctrl-names = "default";
+ pinctrl-0 = <&lptim1_in_pins>;
+ };
+ };
--
1.9.1

2017-06-21 20:05:47

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v2 1/8] dt-bindings: mfd: Add STM32 LPTimer binding

On Wed, 21 Jun 2017 16:30:08 +0200
Fabrice Gasnier <[email protected]> wrote:

> Add documentation for STMicroelectronics STM32 Low-Power Timer binding.
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
Looks sensible to me.

Acked-by: Jonathan Cameron <[email protected]>
> ---
> Changes in v2:
> - Lee's comments: s/Low Power/Low-Power/, remove 0x in example, improve
> properties descriptions
> ---
> .../devicetree/bindings/mfd/stm32-lptimer.txt | 48 ++++++++++++++++++++++
> 1 file changed, 48 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
>
> diff --git a/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt b/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
> new file mode 100644
> index 0000000..af859c8
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
> @@ -0,0 +1,48 @@
> +STMicroelectronics STM32 Low-Power Timer
> +
> +The STM32 Low-Power Timer (LPTIM) is a 16-bit timer that provides several
> +functions:
> +- PWM output (with programmable prescaler, configurable polarity)
> +- Quadrature encoder, counter
> +- Trigger source for STM32 ADC/DAC (LPTIM_OUT)
> +
> +Required properties:
> +- compatible: Must be "st,stm32-lptimer".
> +- reg: Offset and length of the device's register set.
> +- clocks: Phandle to the clock used by the LP Timer module.
> +- clock-names: Must be "mux".
> +- #address-cells: Should be '<1>'.
> +- #size-cells: Should be '<0>'.
> +
> +Optional subnodes:
> +- pwm: See ../pwm/pwm-stm32-lp.txt
> +- counter: See ../iio/timer/stm32-lptimer-cnt.txt
> +- trigger: See ../iio/timer/stm32-lptimer-trigger.txt
> +
> +Example:
> +
> + lptimer1: lptimer@40002400 {
> + compatible = "st,stm32-lptimer";
> + reg = <0x40002400 0x400>;
> + clocks = <&timer_clk>;
> + clock-names = "mux";
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + pwm {
> + compatible = "st,stm32-pwm-lp";
> + pinctrl-names = "default";
> + pinctrl-0 = <&lppwm1_pins>;
> + };
> +
> + trigger@0 {
> + compatible = "st,stm32-lptimer-trigger";
> + reg = <0>;
> + };
> +
> + counter {
> + compatible = "st,stm32-lptimer-counter";
> + pinctrl-names = "default";
> + pinctrl-0 = <&lptim1_in_pins>;
> + };
> + };

2017-06-22 15:44:05

by Lee Jones

[permalink] [raw]
Subject: Re: [PATCH v2 1/8] dt-bindings: mfd: Add STM32 LPTimer binding

On Wed, 21 Jun 2017, Fabrice Gasnier wrote:

> Add documentation for STMicroelectronics STM32 Low-Power Timer binding.
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
> ---
> Changes in v2:
> - Lee's comments: s/Low Power/Low-Power/, remove 0x in example, improve
> properties descriptions
> ---
> .../devicetree/bindings/mfd/stm32-lptimer.txt | 48 ++++++++++++++++++++++
> 1 file changed, 48 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/stm32-lptimer.txt

For my own reference:
Acked-for-MFD-by: Lee Jones <[email protected]>

> diff --git a/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt b/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
> new file mode 100644
> index 0000000..af859c8
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
> @@ -0,0 +1,48 @@
> +STMicroelectronics STM32 Low-Power Timer
> +
> +The STM32 Low-Power Timer (LPTIM) is a 16-bit timer that provides several
> +functions:
> +- PWM output (with programmable prescaler, configurable polarity)
> +- Quadrature encoder, counter
> +- Trigger source for STM32 ADC/DAC (LPTIM_OUT)
> +
> +Required properties:
> +- compatible: Must be "st,stm32-lptimer".
> +- reg: Offset and length of the device's register set.
> +- clocks: Phandle to the clock used by the LP Timer module.
> +- clock-names: Must be "mux".
> +- #address-cells: Should be '<1>'.
> +- #size-cells: Should be '<0>'.
> +
> +Optional subnodes:
> +- pwm: See ../pwm/pwm-stm32-lp.txt
> +- counter: See ../iio/timer/stm32-lptimer-cnt.txt
> +- trigger: See ../iio/timer/stm32-lptimer-trigger.txt
> +
> +Example:
> +
> + lptimer1: lptimer@40002400 {
> + compatible = "st,stm32-lptimer";
> + reg = <0x40002400 0x400>;
> + clocks = <&timer_clk>;
> + clock-names = "mux";
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + pwm {
> + compatible = "st,stm32-pwm-lp";
> + pinctrl-names = "default";
> + pinctrl-0 = <&lppwm1_pins>;
> + };
> +
> + trigger@0 {
> + compatible = "st,stm32-lptimer-trigger";
> + reg = <0>;
> + };
> +
> + counter {
> + compatible = "st,stm32-lptimer-counter";
> + pinctrl-names = "default";
> + pinctrl-0 = <&lptim1_in_pins>;
> + };
> + };

--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

2017-06-22 15:44:49

by Lee Jones

[permalink] [raw]
Subject: Re: [PATCH v2 2/8] mfd: Add STM32 LPTimer driver

On Wed, 21 Jun 2017, Fabrice Gasnier wrote:

> STM32 Low-Power Timer hardware block can be used for:
> - PWM generation
> - IIO trigger (in sync with PWM)
> - IIO quadrature encoder counter
> PWM and IIO timer configuration are mixed in the same registers so
> we need a multi fonction driver to be able to share those registers.
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
> ---
> Changes in v2:
> - Lee's remarks: various comments, max register define, s/Low Power/Low-Power,
> clock name, removed reset, add kernel doc for stm32_lptimer struct
> ---
> drivers/mfd/Kconfig | 14 +++++
> drivers/mfd/Makefile | 1 +
> drivers/mfd/stm32-lptimer.c | 107 ++++++++++++++++++++++++++++++++++++++
> include/linux/mfd/stm32-lptimer.h | 62 ++++++++++++++++++++++
> 4 files changed, 184 insertions(+)
> create mode 100644 drivers/mfd/stm32-lptimer.c
> create mode 100644 include/linux/mfd/stm32-lptimer.h

For my own reference:
Acked-for-MFD-by: Lee Jones <[email protected]>

> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 3eb5c93..8e1ca44 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -1683,6 +1683,20 @@ config MFD_STW481X
> in various ST Microelectronics and ST-Ericsson embedded
> Nomadik series.
>
> +config MFD_STM32_LPTIMER
> + tristate "Support for STM32 Low-Power Timer"
> + depends on (ARCH_STM32 && OF) || COMPILE_TEST
> + select MFD_CORE
> + select REGMAP
> + select REGMAP_MMIO
> + help
> + Select this option to enable STM32 Low-Power Timer driver
> + used for PWM, IIO Trigger, IIO Encoder and Counter. Shared
> + resources are also dealt with here.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called stm32-lptimer.
> +
> config MFD_STM32_TIMERS
> tristate "Support for STM32 Timers"
> depends on (ARCH_STM32 && OF) || COMPILE_TEST
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index c16bf1e..a5308d8 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -219,5 +219,6 @@ obj-$(CONFIG_MFD_MT6397) += mt6397-core.o
> obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o
> obj-$(CONFIG_MFD_SUN4I_GPADC) += sun4i-gpadc.o
>
> +obj-$(CONFIG_MFD_STM32_LPTIMER) += stm32-lptimer.o
> obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o
> obj-$(CONFIG_MFD_MXS_LRADC) += mxs-lradc.o
> diff --git a/drivers/mfd/stm32-lptimer.c b/drivers/mfd/stm32-lptimer.c
> new file mode 100644
> index 0000000..075330a
> --- /dev/null
> +++ b/drivers/mfd/stm32-lptimer.c
> @@ -0,0 +1,107 @@
> +/*
> + * STM32 Low-Power Timer parent driver.
> + *
> + * Copyright (C) STMicroelectronics 2017
> + *
> + * Author: Fabrice Gasnier <[email protected]>
> + *
> + * Inspired by Benjamin Gaignard's stm32-timers driver
> + *
> + * License terms: GNU General Public License (GPL), version 2
> + */
> +
> +#include <linux/mfd/stm32-lptimer.h>
> +#include <linux/module.h>
> +#include <linux/of_platform.h>
> +
> +#define STM32_LPTIM_MAX_REGISTER 0x3fc
> +
> +static const struct regmap_config stm32_lptimer_regmap_cfg = {
> + .reg_bits = 32,
> + .val_bits = 32,
> + .reg_stride = sizeof(u32),
> + .max_register = STM32_LPTIM_MAX_REGISTER,
> +};
> +
> +static int stm32_lptimer_detect_encoder(struct stm32_lptimer *ddata)
> +{
> + u32 val;
> + int ret;
> +
> + /*
> + * Quadrature encoder mode bit can only be written and read back when
> + * Low-Power Timer supports it.
> + */
> + ret = regmap_update_bits(ddata->regmap, STM32_LPTIM_CFGR,
> + STM32_LPTIM_ENC, STM32_LPTIM_ENC);
> + if (ret)
> + return ret;
> +
> + ret = regmap_read(ddata->regmap, STM32_LPTIM_CFGR, &val);
> + if (ret)
> + return ret;
> +
> + ret = regmap_update_bits(ddata->regmap, STM32_LPTIM_CFGR,
> + STM32_LPTIM_ENC, 0);
> + if (ret)
> + return ret;
> +
> + ddata->has_encoder = !!(val & STM32_LPTIM_ENC);
> +
> + return 0;
> +}
> +
> +static int stm32_lptimer_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct stm32_lptimer *ddata;
> + struct resource *res;
> + void __iomem *mmio;
> + int ret;
> +
> + ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
> + if (!ddata)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + mmio = devm_ioremap_resource(dev, res);
> + if (IS_ERR(mmio))
> + return PTR_ERR(mmio);
> +
> + ddata->regmap = devm_regmap_init_mmio_clk(dev, "mux", mmio,
> + &stm32_lptimer_regmap_cfg);
> + if (IS_ERR(ddata->regmap))
> + return PTR_ERR(ddata->regmap);
> +
> + ddata->clk = devm_clk_get(dev, NULL);
> + if (IS_ERR(ddata->clk))
> + return PTR_ERR(ddata->clk);
> +
> + ret = stm32_lptimer_detect_encoder(ddata);
> + if (ret)
> + return ret;
> +
> + platform_set_drvdata(pdev, ddata);
> +
> + return devm_of_platform_populate(&pdev->dev);
> +}
> +
> +static const struct of_device_id stm32_lptimer_of_match[] = {
> + { .compatible = "st,stm32-lptimer", },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, stm32_lptimer_of_match);
> +
> +static struct platform_driver stm32_lptimer_driver = {
> + .probe = stm32_lptimer_probe,
> + .driver = {
> + .name = "stm32-lptimer",
> + .of_match_table = stm32_lptimer_of_match,
> + },
> +};
> +module_platform_driver(stm32_lptimer_driver);
> +
> +MODULE_AUTHOR("Fabrice Gasnier <[email protected]>");
> +MODULE_DESCRIPTION("STMicroelectronics STM32 Low-Power Timer");
> +MODULE_ALIAS("platform:stm32-lptimer");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/mfd/stm32-lptimer.h b/include/linux/mfd/stm32-lptimer.h
> new file mode 100644
> index 0000000..77c7cf4
> --- /dev/null
> +++ b/include/linux/mfd/stm32-lptimer.h
> @@ -0,0 +1,62 @@
> +/*
> + * STM32 Low-Power Timer parent driver.
> + *
> + * Copyright (C) STMicroelectronics 2017
> + *
> + * Author: Fabrice Gasnier <[email protected]>
> + *
> + * Inspired by Benjamin Gaignard's stm32-timers driver
> + *
> + * License terms: GNU General Public License (GPL), version 2
> + */
> +
> +#ifndef _LINUX_STM32_LPTIMER_H_
> +#define _LINUX_STM32_LPTIMER_H_
> +
> +#include <linux/clk.h>
> +#include <linux/regmap.h>
> +
> +#define STM32_LPTIM_ISR 0x00 /* Interrupt and Status Reg */
> +#define STM32_LPTIM_ICR 0x04 /* Interrupt Clear Reg */
> +#define STM32_LPTIM_IER 0x08 /* Interrupt Enable Reg */
> +#define STM32_LPTIM_CFGR 0x0C /* Configuration Reg */
> +#define STM32_LPTIM_CR 0x10 /* Control Reg */
> +#define STM32_LPTIM_CMP 0x14 /* Compare Reg */
> +#define STM32_LPTIM_ARR 0x18 /* Autoreload Reg */
> +#define STM32_LPTIM_CNT 0x1C /* Counter Reg */
> +
> +/* STM32_LPTIM_ISR - bit fields */
> +#define STM32_LPTIM_CMPOK_ARROK GENMASK(4, 3)
> +#define STM32_LPTIM_ARROK BIT(4)
> +#define STM32_LPTIM_CMPOK BIT(3)
> +
> +/* STM32_LPTIM_ICR - bit fields */
> +#define STM32_LPTIM_CMPOKCF_ARROKCF GENMASK(4, 3)
> +
> +/* STM32_LPTIM_CR - bit fields */
> +#define STM32_LPTIM_CNTSTRT BIT(2)
> +#define STM32_LPTIM_ENABLE BIT(0)
> +
> +/* STM32_LPTIM_CFGR - bit fields */
> +#define STM32_LPTIM_ENC BIT(24)
> +#define STM32_LPTIM_COUNTMODE BIT(23)
> +#define STM32_LPTIM_WAVPOL BIT(21)
> +#define STM32_LPTIM_PRESC GENMASK(11, 9)
> +#define STM32_LPTIM_CKPOL GENMASK(2, 1)
> +
> +/* STM32_LPTIM_ARR */
> +#define STM32_LPTIM_MAX_ARR 0xFFFF
> +
> +/**
> + * struct stm32_lptimer - STM32 Low-Power Timer data assigned by parent device
> + * @clk: clock reference for this instance
> + * @regmap: register map reference for this instance
> + * @has_encoder: indicates this Low-Power Timer supports encoder mode
> + */
> +struct stm32_lptimer {
> + struct clk *clk;
> + struct regmap *regmap;
> + bool has_encoder;
> +};
> +
> +#endif

--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

2017-06-24 20:10:50

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v2 5/8] dt-bindings: iio: Add STM32 LPTimer trigger binding

On Wed, 21 Jun 2017 16:30:12 +0200
Fabrice Gasnier <[email protected]> wrote:

> Add documentation for STMicroelectronics STM32 Low-Power Timer Trigger
> binding.
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
looks sensible to me.
I'm kind of assuming this whole series will end up going through Lee and MFD.

Acked-by: Jonathan Cameron <[email protected]>
> ---
> Changes in v2:
> - s/Low Power/Low-Power
> - remove leading 0x in example (parent node)
> - improve reg property description
> ---
> .../bindings/iio/timer/stm32-lptimer-trigger.txt | 23 ++++++++++++++++++++++
> 1 file changed, 23 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt
>
> diff --git a/Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt b/Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt
> new file mode 100644
> index 0000000..466d99f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt
> @@ -0,0 +1,23 @@
> +STMicroelectronics STM32 Low-Power Timer Trigger
> +
> +STM32 Low-Power Timer provides trigger source (LPTIM output) that can be used
> +by STM32 internal ADC and/or DAC.
> +
> +Must be a sub-node of an STM32 Low-Power Timer device tree node.
> +See ../mfd/stm32-lptimer.txt for details about the parent node.
> +
> +Required properties:
> +- compatible: Must be "st,stm32-lptimer-trigger".
> +- reg: Selects trigger hardware block. Must be 0, 1 or 2
> + respectively for lptimer1, lptimer2 or lptimer3
> + trigger output.
> +
> +Example:
> + lptimer1: lptimer@40002400 {
> + compatible = "st,stm32-lptimer";
> + ...
> + trigger@0 {
> + compatible = "st,stm32-lptimer-trigger";
> + reg = <0>;
> + };
> + };

2017-06-24 20:13:44

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v2 6/8] iio: trigger: Add STM32 LPTimer trigger driver

On Wed, 21 Jun 2017 16:30:13 +0200
Fabrice Gasnier <[email protected]> wrote:

> Add support for LPTIMx_OUT triggers that can be found on some STM32
> devices. These triggers can be used then by ADC or DAC.
> Typical usage is to configure LPTimer as PWM output (via pwm-stm32-lp)
> and have synchronised analog conversions with these triggers.
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
Given this can't be used as a trigger for other devices (no exposed
interrupt?) I'd expect to see a validate_device callback provided for
the trigger ops. That would prevent other devices trying to use it.

Otherwise, looks good.

Jonathan
> ---
> Changes in v2:
> - s/Low Power/Low-Power
> - update few comments
> ---
> drivers/iio/trigger/Kconfig | 11 +++
> drivers/iio/trigger/Makefile | 1 +
> drivers/iio/trigger/stm32-lptimer-trigger.c | 110 ++++++++++++++++++++++++++
> include/linux/iio/timer/stm32-lptim-trigger.h | 24 ++++++
> 4 files changed, 146 insertions(+)
> create mode 100644 drivers/iio/trigger/stm32-lptimer-trigger.c
> create mode 100644 include/linux/iio/timer/stm32-lptim-trigger.h
>
> diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig
> index e4d4e63..a633d2c 100644
> --- a/drivers/iio/trigger/Kconfig
> +++ b/drivers/iio/trigger/Kconfig
> @@ -24,6 +24,17 @@ config IIO_INTERRUPT_TRIGGER
> To compile this driver as a module, choose M here: the
> module will be called iio-trig-interrupt.
>
> +config IIO_STM32_LPTIMER_TRIGGER
> + tristate "STM32 Low-Power Timer Trigger"
> + depends on MFD_STM32_LPTIMER || COMPILE_TEST
> + help
> + Select this option to enable STM32 Low-Power Timer Trigger.
> + This can be used as trigger source for STM32 internal ADC
> + and/or DAC.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called stm32-lptimer-trigger.
> +
> config IIO_STM32_TIMER_TRIGGER
> tristate "STM32 Timer Trigger"
> depends on (ARCH_STM32 && OF && MFD_STM32_TIMERS) || COMPILE_TEST
> diff --git a/drivers/iio/trigger/Makefile b/drivers/iio/trigger/Makefile
> index 5c4ecd3..0a72a2a 100644
> --- a/drivers/iio/trigger/Makefile
> +++ b/drivers/iio/trigger/Makefile
> @@ -6,6 +6,7 @@
>
> obj-$(CONFIG_IIO_HRTIMER_TRIGGER) += iio-trig-hrtimer.o
> obj-$(CONFIG_IIO_INTERRUPT_TRIGGER) += iio-trig-interrupt.o
> +obj-$(CONFIG_IIO_STM32_LPTIMER_TRIGGER) += stm32-lptimer-trigger.o
> obj-$(CONFIG_IIO_STM32_TIMER_TRIGGER) += stm32-timer-trigger.o
> obj-$(CONFIG_IIO_SYSFS_TRIGGER) += iio-trig-sysfs.o
> obj-$(CONFIG_IIO_TIGHTLOOP_TRIGGER) += iio-trig-loop.o
> diff --git a/drivers/iio/trigger/stm32-lptimer-trigger.c b/drivers/iio/trigger/stm32-lptimer-trigger.c
> new file mode 100644
> index 0000000..bcb9aa2
> --- /dev/null
> +++ b/drivers/iio/trigger/stm32-lptimer-trigger.c
> @@ -0,0 +1,110 @@
> +/*
> + * STM32 Low-Power Timer Trigger driver
> + *
> + * Copyright (C) STMicroelectronics 2017
> + *
> + * Author: Fabrice Gasnier <[email protected]>.
> + *
> + * License terms: GNU General Public License (GPL), version 2
> + *
> + * Inspired by Benjamin Gaignard's stm32-timer-trigger driver
> + */
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/timer/stm32-lptim-trigger.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/mfd/stm32-lptimer.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +
> +/* List Low-Power Timer triggers */
> +static const char * const stm32_lptim_triggers[] = {
> + LPTIM1_OUT,
> + LPTIM2_OUT,
> + LPTIM3_OUT,
> +};
> +
> +struct stm32_lptim_trigger {
> + struct device *dev;
> + const char *trg;
> +};
> +
> +static const struct iio_trigger_ops stm32_lptim_trigger_ops = {
> + .owner = THIS_MODULE,
> +};
> +
> +/**
> + * is_stm32_lptim_trigger
> + * @trig: trigger to be checked
> + *
> + * return true if the trigger is a valid STM32 IIO Low-Power Timer Trigger
> + * either return false
> + */
> +bool is_stm32_lptim_trigger(struct iio_trigger *trig)
> +{
> + return (trig->ops == &stm32_lptim_trigger_ops);
> +}
> +EXPORT_SYMBOL(is_stm32_lptim_trigger);
> +
> +static int stm32_lptim_setup_trig(struct stm32_lptim_trigger *priv)
> +{
> + struct iio_trigger *trig;
> +
> + trig = devm_iio_trigger_alloc(priv->dev, "%s", priv->trg);
> + if (!trig)
> + return -ENOMEM;
> +
> + trig->dev.parent = priv->dev->parent;
> + trig->ops = &stm32_lptim_trigger_ops;
> + iio_trigger_set_drvdata(trig, priv);
> +
> + return devm_iio_trigger_register(priv->dev, trig);
> +}
> +
> +static int stm32_lptim_trigger_probe(struct platform_device *pdev)
> +{
> + struct stm32_lptim_trigger *priv;
> + u32 index;
> + int ret;
> +
> + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + if (of_property_read_u32(pdev->dev.of_node, "reg", &index))
> + return -EINVAL;
> +
> + if (index >= ARRAY_SIZE(stm32_lptim_triggers))
> + return -EINVAL;
> +
> + priv->dev = &pdev->dev;
> + priv->trg = stm32_lptim_triggers[index];
> +
> + ret = stm32_lptim_setup_trig(priv);
> + if (ret)
> + return ret;
> +
> + platform_set_drvdata(pdev, priv);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id stm32_lptim_trig_of_match[] = {
> + { .compatible = "st,stm32-lptimer-trigger", },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, stm32_lptim_trig_of_match);
> +
> +static struct platform_driver stm32_lptim_trigger_driver = {
> + .probe = stm32_lptim_trigger_probe,
> + .driver = {
> + .name = "stm32-lptimer-trigger",
> + .of_match_table = stm32_lptim_trig_of_match,
> + },
> +};
> +module_platform_driver(stm32_lptim_trigger_driver);
> +
> +MODULE_AUTHOR("Fabrice Gasnier <[email protected]>");
> +MODULE_ALIAS("platform:stm32-lptimer-trigger");
> +MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM trigger driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/iio/timer/stm32-lptim-trigger.h b/include/linux/iio/timer/stm32-lptim-trigger.h
> new file mode 100644
> index 0000000..cb795b1
> --- /dev/null
> +++ b/include/linux/iio/timer/stm32-lptim-trigger.h
> @@ -0,0 +1,24 @@
> +/*
> + * Copyright (C) STMicroelectronics 2017
> + *
> + * Author: Fabrice Gasnier <[email protected]>
> + *
> + * License terms: GNU General Public License (GPL), version 2
> + */
> +
> +#ifndef _STM32_LPTIM_TRIGGER_H_
> +#define _STM32_LPTIM_TRIGGER_H_
> +
> +#define LPTIM1_OUT "lptim1_out"
> +#define LPTIM2_OUT "lptim2_out"
> +#define LPTIM3_OUT "lptim3_out"
> +
> +#if IS_ENABLED(CONFIG_IIO_STM32_LPTIMER_TRIGGER)
> +bool is_stm32_lptim_trigger(struct iio_trigger *trig);
> +#else
> +static inline bool is_stm32_lptim_trigger(struct iio_trigger *trig)
> +{
> + return false;
> +}
> +#endif
> +#endif

2017-06-24 20:14:50

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v2 7/8] dt-bindings: iio: Add STM32 LPTimer quadrature encoder and counter

On Wed, 21 Jun 2017 16:30:14 +0200
Fabrice Gasnier <[email protected]> wrote:

> Add documentation for STMicroelectronics STM32 Low-Power Timer
> quadrature encoder and counter binding.
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
Acked-by: Jonathan Cameron <[email protected]>
> ---
> Changes in v2:
> - s/Low Power/Low-Power
> - remove leading 0x in example (parent node)
> - improve properties descriptions
> ---
> .../bindings/iio/counter/stm32-lptimer-cnt.txt | 27 ++++++++++++++++++++++
> 1 file changed, 27 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/counter/stm32-lptimer-cnt.txt
>
> diff --git a/Documentation/devicetree/bindings/iio/counter/stm32-lptimer-cnt.txt b/Documentation/devicetree/bindings/iio/counter/stm32-lptimer-cnt.txt
> new file mode 100644
> index 0000000..b9fd014
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/counter/stm32-lptimer-cnt.txt
> @@ -0,0 +1,27 @@
> +STMicroelectronics STM32 Low-Power Timer quadrature encoder and counter
> +
> +STM32 Low-Power Timer provides several counter modes. It can be used as:
> +- quadrature encoder to detect angular position and direction of rotary
> + elements, from IN1 and IN2 input signals.
> +- simple counter from IN1 input signal.
> +
> +Must be a sub-node of an STM32 Low-Power Timer device tree node.
> +See ../mfd/stm32-lptimer.txt for details about the parent node.
> +
> +Required properties:
> +- compatible: Must be "st,stm32-lptimer-counter".
> +- pinctrl-names: Set to "default".
> +- pinctrl-0: List of phandles pointing to pin configuration nodes,
> + to set IN1/IN2 pins in mode of operation for Low-Power
> + Timer input on external pin.
> +
> +Example:
> + lptimer1: lptimer@40002400 {
> + compatible = "st,stm32-lptimer";
> + ...
> + counter {
> + compatible = "st,stm32-lptimer-counter";
> + pinctrl-names = "default";
> + pinctrl-0 = <&lptim1_in_pins>;
> + };
> + };

2017-06-24 20:35:49

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v2 8/8] iio: counter: Add support for STM32 LPTimer

On Wed, 21 Jun 2017 16:30:15 +0200
Fabrice Gasnier <[email protected]> wrote:

> Add support for STM32 Low-Power Timer, that can be used as counter
> or quadrature encoder.
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
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: [email protected]
> +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: [email protected]
> +Description:
> + Reading returns the list possible quadrature modes.
> +
> +What: /sys/bus/iio/devices/iio:deviceX/in_count0_quadrature_mode
> +KernelVersion: 4.13
> +Contact: [email protected]
> +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: [email protected]
> +Description:
> + Reading returns the list possible active edges.
> +
> +What: /sys/bus/iio/devices/iio:deviceX/in_count0_polarity
> +KernelVersion: 4.13
> +Contact: [email protected]
> +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?
> 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 <[email protected]>
> + *
> + * 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 <[email protected]>");
> +MODULE_ALIAS("platform:stm32-lptimer-counter");
> +MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
> +MODULE_LICENSE("GPL v2");

2017-06-26 16:42:18

by Fabrice Gasnier

[permalink] [raw]
Subject: Re: [PATCH v2 6/8] iio: trigger: Add STM32 LPTimer trigger driver

On 06/24/2017 10:13 PM, Jonathan Cameron wrote:
> On Wed, 21 Jun 2017 16:30:13 +0200
> Fabrice Gasnier <[email protected]> wrote:
>
>> Add support for LPTIMx_OUT triggers that can be found on some STM32
>> devices. These triggers can be used then by ADC or DAC.
>> Typical usage is to configure LPTimer as PWM output (via pwm-stm32-lp)
>> and have synchronised analog conversions with these triggers.
>>
>> Signed-off-by: Fabrice Gasnier <[email protected]>
> Given this can't be used as a trigger for other devices (no exposed
> interrupt?) I'd expect to see a validate_device callback provided for
> the trigger ops. That would prevent other devices trying to use it.

Hi Jonathan,

This is something I had in mind also earlier. Only thing is...
Basically, this is limiting: when trigger poll happens on device side
(e.g. ADC), another device could use same trigger. But I admit this
looks like corner case.

I'll add it in next version, with additional patch for ADC part to
validate it's a valid device (No DAC yet).
I think I'll use INDIO_HARDWARE_TRIGGERED mode:
- in adc driver: indio_dev->modes |= INDIO_HARDWARE_TRIGGERED;
- in lptimer: if (indio_dev->modes & INDIO_HARDWARE_TRIGGERED)...

>
> Otherwise, looks good.

Many thanks for your review.
Best Regards,
Fabrice

>
> Jonathan
>> ---
>> Changes in v2:
>> - s/Low Power/Low-Power
>> - update few comments
>> ---
>> drivers/iio/trigger/Kconfig | 11 +++
>> drivers/iio/trigger/Makefile | 1 +
>> drivers/iio/trigger/stm32-lptimer-trigger.c | 110 ++++++++++++++++++++++++++
>> include/linux/iio/timer/stm32-lptim-trigger.h | 24 ++++++
>> 4 files changed, 146 insertions(+)
>> create mode 100644 drivers/iio/trigger/stm32-lptimer-trigger.c
>> create mode 100644 include/linux/iio/timer/stm32-lptim-trigger.h
>>
>> diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig
>> index e4d4e63..a633d2c 100644
>> --- a/drivers/iio/trigger/Kconfig
>> +++ b/drivers/iio/trigger/Kconfig
>> @@ -24,6 +24,17 @@ config IIO_INTERRUPT_TRIGGER
>> To compile this driver as a module, choose M here: the
>> module will be called iio-trig-interrupt.
>>
>> +config IIO_STM32_LPTIMER_TRIGGER
>> + tristate "STM32 Low-Power Timer Trigger"
>> + depends on MFD_STM32_LPTIMER || COMPILE_TEST
>> + help
>> + Select this option to enable STM32 Low-Power Timer Trigger.
>> + This can be used as trigger source for STM32 internal ADC
>> + and/or DAC.
>> +
>> + To compile this driver as a module, choose M here: the
>> + module will be called stm32-lptimer-trigger.
>> +
>> config IIO_STM32_TIMER_TRIGGER
>> tristate "STM32 Timer Trigger"
>> depends on (ARCH_STM32 && OF && MFD_STM32_TIMERS) || COMPILE_TEST
>> diff --git a/drivers/iio/trigger/Makefile b/drivers/iio/trigger/Makefile
>> index 5c4ecd3..0a72a2a 100644
>> --- a/drivers/iio/trigger/Makefile
>> +++ b/drivers/iio/trigger/Makefile
>> @@ -6,6 +6,7 @@
>>
>> obj-$(CONFIG_IIO_HRTIMER_TRIGGER) += iio-trig-hrtimer.o
>> obj-$(CONFIG_IIO_INTERRUPT_TRIGGER) += iio-trig-interrupt.o
>> +obj-$(CONFIG_IIO_STM32_LPTIMER_TRIGGER) += stm32-lptimer-trigger.o
>> obj-$(CONFIG_IIO_STM32_TIMER_TRIGGER) += stm32-timer-trigger.o
>> obj-$(CONFIG_IIO_SYSFS_TRIGGER) += iio-trig-sysfs.o
>> obj-$(CONFIG_IIO_TIGHTLOOP_TRIGGER) += iio-trig-loop.o
>> diff --git a/drivers/iio/trigger/stm32-lptimer-trigger.c b/drivers/iio/trigger/stm32-lptimer-trigger.c
>> new file mode 100644
>> index 0000000..bcb9aa2
>> --- /dev/null
>> +++ b/drivers/iio/trigger/stm32-lptimer-trigger.c
>> @@ -0,0 +1,110 @@
>> +/*
>> + * STM32 Low-Power Timer Trigger driver
>> + *
>> + * Copyright (C) STMicroelectronics 2017
>> + *
>> + * Author: Fabrice Gasnier <[email protected]>.
>> + *
>> + * License terms: GNU General Public License (GPL), version 2
>> + *
>> + * Inspired by Benjamin Gaignard's stm32-timer-trigger driver
>> + */
>> +
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/timer/stm32-lptim-trigger.h>
>> +#include <linux/iio/trigger.h>
>> +#include <linux/mfd/stm32-lptimer.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +
>> +/* List Low-Power Timer triggers */
>> +static const char * const stm32_lptim_triggers[] = {
>> + LPTIM1_OUT,
>> + LPTIM2_OUT,
>> + LPTIM3_OUT,
>> +};
>> +
>> +struct stm32_lptim_trigger {
>> + struct device *dev;
>> + const char *trg;
>> +};
>> +
>> +static const struct iio_trigger_ops stm32_lptim_trigger_ops = {
>> + .owner = THIS_MODULE,
>> +};
>> +
>> +/**
>> + * is_stm32_lptim_trigger
>> + * @trig: trigger to be checked
>> + *
>> + * return true if the trigger is a valid STM32 IIO Low-Power Timer Trigger
>> + * either return false
>> + */
>> +bool is_stm32_lptim_trigger(struct iio_trigger *trig)
>> +{
>> + return (trig->ops == &stm32_lptim_trigger_ops);
>> +}
>> +EXPORT_SYMBOL(is_stm32_lptim_trigger);
>> +
>> +static int stm32_lptim_setup_trig(struct stm32_lptim_trigger *priv)
>> +{
>> + struct iio_trigger *trig;
>> +
>> + trig = devm_iio_trigger_alloc(priv->dev, "%s", priv->trg);
>> + if (!trig)
>> + return -ENOMEM;
>> +
>> + trig->dev.parent = priv->dev->parent;
>> + trig->ops = &stm32_lptim_trigger_ops;
>> + iio_trigger_set_drvdata(trig, priv);
>> +
>> + return devm_iio_trigger_register(priv->dev, trig);
>> +}
>> +
>> +static int stm32_lptim_trigger_probe(struct platform_device *pdev)
>> +{
>> + struct stm32_lptim_trigger *priv;
>> + u32 index;
>> + int ret;
>> +
>> + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
>> + if (!priv)
>> + return -ENOMEM;
>> +
>> + if (of_property_read_u32(pdev->dev.of_node, "reg", &index))
>> + return -EINVAL;
>> +
>> + if (index >= ARRAY_SIZE(stm32_lptim_triggers))
>> + return -EINVAL;
>> +
>> + priv->dev = &pdev->dev;
>> + priv->trg = stm32_lptim_triggers[index];
>> +
>> + ret = stm32_lptim_setup_trig(priv);
>> + if (ret)
>> + return ret;
>> +
>> + platform_set_drvdata(pdev, priv);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct of_device_id stm32_lptim_trig_of_match[] = {
>> + { .compatible = "st,stm32-lptimer-trigger", },
>> + {},
>> +};
>> +MODULE_DEVICE_TABLE(of, stm32_lptim_trig_of_match);
>> +
>> +static struct platform_driver stm32_lptim_trigger_driver = {
>> + .probe = stm32_lptim_trigger_probe,
>> + .driver = {
>> + .name = "stm32-lptimer-trigger",
>> + .of_match_table = stm32_lptim_trig_of_match,
>> + },
>> +};
>> +module_platform_driver(stm32_lptim_trigger_driver);
>> +
>> +MODULE_AUTHOR("Fabrice Gasnier <[email protected]>");
>> +MODULE_ALIAS("platform:stm32-lptimer-trigger");
>> +MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM trigger driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/include/linux/iio/timer/stm32-lptim-trigger.h b/include/linux/iio/timer/stm32-lptim-trigger.h
>> new file mode 100644
>> index 0000000..cb795b1
>> --- /dev/null
>> +++ b/include/linux/iio/timer/stm32-lptim-trigger.h
>> @@ -0,0 +1,24 @@
>> +/*
>> + * Copyright (C) STMicroelectronics 2017
>> + *
>> + * Author: Fabrice Gasnier <[email protected]>
>> + *
>> + * License terms: GNU General Public License (GPL), version 2
>> + */
>> +
>> +#ifndef _STM32_LPTIM_TRIGGER_H_
>> +#define _STM32_LPTIM_TRIGGER_H_
>> +
>> +#define LPTIM1_OUT "lptim1_out"
>> +#define LPTIM2_OUT "lptim2_out"
>> +#define LPTIM3_OUT "lptim3_out"
>> +
>> +#if IS_ENABLED(CONFIG_IIO_STM32_LPTIMER_TRIGGER)
>> +bool is_stm32_lptim_trigger(struct iio_trigger *trig);
>> +#else
>> +static inline bool is_stm32_lptim_trigger(struct iio_trigger *trig)
>> +{
>> + return false;
>> +}
>> +#endif
>> +#endif
>

2017-06-26 18:07:53

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v2 1/8] dt-bindings: mfd: Add STM32 LPTimer binding

On Wed, Jun 21, 2017 at 04:30:08PM +0200, Fabrice Gasnier wrote:
> Add documentation for STMicroelectronics STM32 Low-Power Timer binding.
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
> ---
> Changes in v2:
> - Lee's comments: s/Low Power/Low-Power/, remove 0x in example, improve
> properties descriptions
> ---
> .../devicetree/bindings/mfd/stm32-lptimer.txt | 48 ++++++++++++++++++++++
> 1 file changed, 48 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
>
> diff --git a/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt b/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
> new file mode 100644
> index 0000000..af859c8
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
> @@ -0,0 +1,48 @@
> +STMicroelectronics STM32 Low-Power Timer
> +
> +The STM32 Low-Power Timer (LPTIM) is a 16-bit timer that provides several
> +functions:
> +- PWM output (with programmable prescaler, configurable polarity)
> +- Quadrature encoder, counter
> +- Trigger source for STM32 ADC/DAC (LPTIM_OUT)
> +
> +Required properties:
> +- compatible: Must be "st,stm32-lptimer".
> +- reg: Offset and length of the device's register set.
> +- clocks: Phandle to the clock used by the LP Timer module.
> +- clock-names: Must be "mux".
> +- #address-cells: Should be '<1>'.
> +- #size-cells: Should be '<0>'.
> +
> +Optional subnodes:
> +- pwm: See ../pwm/pwm-stm32-lp.txt
> +- counter: See ../iio/timer/stm32-lptimer-cnt.txt
> +- trigger: See ../iio/timer/stm32-lptimer-trigger.txt
> +
> +Example:
> +
> + lptimer1: lptimer@40002400 {

timer@...

> + compatible = "st,stm32-lptimer";
> + reg = <0x40002400 0x400>;
> + clocks = <&timer_clk>;
> + clock-names = "mux";
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + pwm {
> + compatible = "st,stm32-pwm-lp";
> + pinctrl-names = "default";
> + pinctrl-0 = <&lppwm1_pins>;
> + };
> +
> + trigger@0 {
> + compatible = "st,stm32-lptimer-trigger";
> + reg = <0>;

Is there more than 1?

> + };
> +
> + counter {
> + compatible = "st,stm32-lptimer-counter";
> + pinctrl-names = "default";
> + pinctrl-0 = <&lptim1_in_pins>;
> + };
> + };
> --
> 1.9.1
>

2017-06-26 18:10:13

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v2 3/8] dt-bindings: pwm: Add STM32 LPTimer PWM binding

On Wed, Jun 21, 2017 at 04:30:10PM +0200, Fabrice Gasnier wrote:
> Add documentation for STMicroelectronics STM32 Low-Power Timer
> PWM binding.
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
> ---
> Changes in v2:
> - s/Low Power/Low-Power/
> - remove 0x in example (parent node)
> - improve properties description
> ---
> .../devicetree/bindings/pwm/pwm-stm32-lp.txt | 24 ++++++++++++++++++++++
> 1 file changed, 24 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/pwm/pwm-stm32-lp.txt
>
> diff --git a/Documentation/devicetree/bindings/pwm/pwm-stm32-lp.txt b/Documentation/devicetree/bindings/pwm/pwm-stm32-lp.txt
> new file mode 100644
> index 0000000..941c990
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pwm/pwm-stm32-lp.txt
> @@ -0,0 +1,24 @@
> +STMicroelectronics STM32 Low-Power Timer PWM
> +
> +STM32 Low-Power Timer provides single channel PWM.
> +
> +Must be a sub-node of an STM32 Low-Power Timer device tree node.
> +See ../mfd/stm32-lptimer.txt for details about the parent node.
> +
> +Required parameters:
> +- compatible: Must be "st,stm32-pwm-lp".
> +
> +Optional properties:
> +- pinctrl-names: Set to "default".
> +- pinctrl-0: Phandle pointing to pin configuration node for PWM.
> +
> +Example:
> + lptimer1: lptimer@40002400 {

timer@...

With that,

Acked-by: Rob Herring <[email protected]>

2017-06-26 18:14:21

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v2 5/8] dt-bindings: iio: Add STM32 LPTimer trigger binding

On Wed, Jun 21, 2017 at 04:30:12PM +0200, Fabrice Gasnier wrote:
> Add documentation for STMicroelectronics STM32 Low-Power Timer Trigger
> binding.
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
> ---
> Changes in v2:
> - s/Low Power/Low-Power
> - remove leading 0x in example (parent node)
> - improve reg property description
> ---
> .../bindings/iio/timer/stm32-lptimer-trigger.txt | 23 ++++++++++++++++++++++
> 1 file changed, 23 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt
>
> diff --git a/Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt b/Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt
> new file mode 100644
> index 0000000..466d99f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt
> @@ -0,0 +1,23 @@
> +STMicroelectronics STM32 Low-Power Timer Trigger
> +
> +STM32 Low-Power Timer provides trigger source (LPTIM output) that can be used
> +by STM32 internal ADC and/or DAC.
> +
> +Must be a sub-node of an STM32 Low-Power Timer device tree node.
> +See ../mfd/stm32-lptimer.txt for details about the parent node.
> +
> +Required properties:
> +- compatible: Must be "st,stm32-lptimer-trigger".
> +- reg: Selects trigger hardware block. Must be 0, 1 or 2
> + respectively for lptimer1, lptimer2 or lptimer3
> + trigger output.

I guess this answers my question. However, this seems like abuse of the
reg prop. This should not be how you select a trigger. The DT should
describe all the h/w blocks and then the adc connection is a separate
property.

Rob

2017-06-26 18:17:05

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v2 7/8] dt-bindings: iio: Add STM32 LPTimer quadrature encoder and counter

On Wed, Jun 21, 2017 at 04:30:14PM +0200, Fabrice Gasnier wrote:
> Add documentation for STMicroelectronics STM32 Low-Power Timer
> quadrature encoder and counter binding.
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
> ---
> Changes in v2:
> - s/Low Power/Low-Power
> - remove leading 0x in example (parent node)
> - improve properties descriptions
> ---
> .../bindings/iio/counter/stm32-lptimer-cnt.txt | 27 ++++++++++++++++++++++
> 1 file changed, 27 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/counter/stm32-lptimer-cnt.txt
>
> diff --git a/Documentation/devicetree/bindings/iio/counter/stm32-lptimer-cnt.txt b/Documentation/devicetree/bindings/iio/counter/stm32-lptimer-cnt.txt
> new file mode 100644
> index 0000000..b9fd014
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/counter/stm32-lptimer-cnt.txt
> @@ -0,0 +1,27 @@
> +STMicroelectronics STM32 Low-Power Timer quadrature encoder and counter
> +
> +STM32 Low-Power Timer provides several counter modes. It can be used as:
> +- quadrature encoder to detect angular position and direction of rotary
> + elements, from IN1 and IN2 input signals.
> +- simple counter from IN1 input signal.
> +
> +Must be a sub-node of an STM32 Low-Power Timer device tree node.
> +See ../mfd/stm32-lptimer.txt for details about the parent node.
> +
> +Required properties:
> +- compatible: Must be "st,stm32-lptimer-counter".
> +- pinctrl-names: Set to "default".
> +- pinctrl-0: List of phandles pointing to pin configuration nodes,
> + to set IN1/IN2 pins in mode of operation for Low-Power
> + Timer input on external pin.
> +
> +Example:
> + lptimer1: lptimer@40002400 {

timer@...

With that,

Acked-by: Rob Herring <[email protected]>

2017-06-26 20:30:17

by William Breathitt Gray

[permalink] [raw]
Subject: Re: [PATCH v2 8/8] iio: counter: Add support for STM32 LPTimer

On Sat, Jun 24, 2017 at 09:35:39PM +0100, Jonathan Cameron wrote:
>On Wed, 21 Jun 2017 16:30:15 +0200
>Fabrice Gasnier <[email protected]> wrote:
>
>> Add support for STM32 Low-Power Timer, that can be used as counter
>> or quadrature encoder.
>>
>> Signed-off-by: Fabrice Gasnier <[email protected]>
>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: [email protected]
>> +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: [email protected]
>> +Description:
>> + Reading returns the list possible quadrature modes.
>> +
>> +What: /sys/bus/iio/devices/iio:deviceX/in_count0_quadrature_mode
>> +KernelVersion: 4.13
>> +Contact: [email protected]
>> +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: [email protected]
>> +Description:
>> + Reading returns the list possible active edges.
>> +
>> +What: /sys/bus/iio/devices/iio:deviceX/in_count0_polarity
>> +KernelVersion: 4.13
>> +Contact: [email protected]
>> +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.

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.

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.

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 <[email protected]>
>> + *
>> + * 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 <[email protected]>");
>> +MODULE_ALIAS("platform:stm32-lptimer-counter");
>> +MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
>> +MODULE_LICENSE("GPL v2");
>

2017-06-26 20:38:17

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v2 5/8] dt-bindings: iio: Add STM32 LPTimer trigger binding



On 26 June 2017 19:14:16 BST, Rob Herring <[email protected]> wrote:
>On Wed, Jun 21, 2017 at 04:30:12PM +0200, Fabrice Gasnier wrote:
>> Add documentation for STMicroelectronics STM32 Low-Power Timer
>Trigger
>> binding.
>>
>> Signed-off-by: Fabrice Gasnier <[email protected]>
>> ---
>> Changes in v2:
>> - s/Low Power/Low-Power
>> - remove leading 0x in example (parent node)
>> - improve reg property description
>> ---
>> .../bindings/iio/timer/stm32-lptimer-trigger.txt | 23
>++++++++++++++++++++++
>> 1 file changed, 23 insertions(+)
>> create mode 100644
>Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt
>>
>> diff --git
>a/Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt
>b/Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt
>> new file mode 100644
>> index 0000000..466d99f
>> --- /dev/null
>> +++
>b/Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt
>> @@ -0,0 +1,23 @@
>> +STMicroelectronics STM32 Low-Power Timer Trigger
>> +
>> +STM32 Low-Power Timer provides trigger source (LPTIM output) that
>can be used
>> +by STM32 internal ADC and/or DAC.
>> +
>> +Must be a sub-node of an STM32 Low-Power Timer device tree node.
>> +See ../mfd/stm32-lptimer.txt for details about the parent node.
>> +
>> +Required properties:
>> +- compatible: Must be "st,stm32-lptimer-trigger".
>> +- reg: Selects trigger hardware block. Must be 0, 1 or 2
>> + respectively for lptimer1, lptimer2 or lptimer3
>> + trigger output.
>
>I guess this answers my question. However, this seems like abuse of the
>
>reg prop. This should not be how you select a trigger. The DT should
>describe all the h/w blocks and then the adc connection is a separate
>property.
I think this is miss described. Reg is labelling instances of the hardware block which is providing the trigger.

The connection to an ADC etc is done from userspace.


>
>Rob
>--
>To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>the body of a message to [email protected]
>More majordomo info at http://vger.kernel.org/majordomo-info.html

--
Sent from my Android device with K-9 Mail. Please excuse my brevity.

2017-06-27 08:22:15

by Benjamin Gaignard

[permalink] [raw]
Subject: Re: [PATCH v2 8/8] iio: counter: Add support for STM32 LPTimer

2017-06-26 22:29 GMT+02:00 William Breathitt Gray <[email protected]>:
> On Sat, Jun 24, 2017 at 09:35:39PM +0100, Jonathan Cameron wrote:
>>On Wed, 21 Jun 2017 16:30:15 +0200
>>Fabrice Gasnier <[email protected]> wrote:
>>
>>> Add support for STM32 Low-Power Timer, that can be used as counter
>>> or quadrature encoder.
>>>
>>> Signed-off-by: Fabrice Gasnier <[email protected]>
>>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: [email protected]
>>> +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: [email protected]
>>> +Description:
>>> + Reading returns the list possible quadrature modes.
>>> +
>>> +What: /sys/bus/iio/devices/iio:deviceX/in_count0_quadrature_mode
>>> +KernelVersion: 4.13
>>> +Contact: [email protected]
>>> +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: [email protected]
>>> +Description:
>>> + Reading returns the list possible active edges.
>>> +
>>> +What: /sys/bus/iio/devices/iio:deviceX/in_count0_polarity
>>> +KernelVersion: 4.13
>>> +Contact: [email protected]
>>> +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 <[email protected]>
>>> + *
>>> + * 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 <[email protected]>");
>>> +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

2017-06-27 08:59:01

by Fabrice Gasnier

[permalink] [raw]
Subject: Re: [PATCH v2 1/8] dt-bindings: mfd: Add STM32 LPTimer binding

On 06/26/2017 08:07 PM, Rob Herring wrote:
> On Wed, Jun 21, 2017 at 04:30:08PM +0200, Fabrice Gasnier wrote:
>> Add documentation for STMicroelectronics STM32 Low-Power Timer binding.
>>
>> Signed-off-by: Fabrice Gasnier <[email protected]>
>> ---
>> Changes in v2:
>> - Lee's comments: s/Low Power/Low-Power/, remove 0x in example, improve
>> properties descriptions
>> ---
>> .../devicetree/bindings/mfd/stm32-lptimer.txt | 48 ++++++++++++++++++++++
>> 1 file changed, 48 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
>>
>> diff --git a/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt b/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
>> new file mode 100644
>> index 0000000..af859c8
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
>> @@ -0,0 +1,48 @@
>> +STMicroelectronics STM32 Low-Power Timer
>> +
>> +The STM32 Low-Power Timer (LPTIM) is a 16-bit timer that provides several
>> +functions:
>> +- PWM output (with programmable prescaler, configurable polarity)
>> +- Quadrature encoder, counter
>> +- Trigger source for STM32 ADC/DAC (LPTIM_OUT)
>> +
>> +Required properties:
>> +- compatible: Must be "st,stm32-lptimer".
>> +- reg: Offset and length of the device's register set.
>> +- clocks: Phandle to the clock used by the LP Timer module.
>> +- clock-names: Must be "mux".
>> +- #address-cells: Should be '<1>'.
>> +- #size-cells: Should be '<0>'.
>> +
>> +Optional subnodes:
>> +- pwm: See ../pwm/pwm-stm32-lp.txt
>> +- counter: See ../iio/timer/stm32-lptimer-cnt.txt
>> +- trigger: See ../iio/timer/stm32-lptimer-trigger.txt
>> +
>> +Example:
>> +
>> + lptimer1: lptimer@40002400 {
>
> timer@...

Hi Rob,

I initially put "lptimer" here to distinguish Low-Power Timer hardware
from other "timers" hardware that can be found also on stm32 devices.
I'd prefer to keep it, is it sensible from your point of view ?
Please advise.

>
>> + compatible = "st,stm32-lptimer";
>> + reg = <0x40002400 0x400>;
>> + clocks = <&timer_clk>;
>> + clock-names = "mux";
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> +
>> + pwm {
>> + compatible = "st,stm32-pwm-lp";
>> + pinctrl-names = "default";
>> + pinctrl-0 = <&lppwm1_pins>;
>> + };
>> +
>> + trigger@0 {
>> + compatible = "st,stm32-lptimer-trigger";
>> + reg = <0>;
>
> Is there more than 1?

reg identifies trigger hardware block.

Best Regards,
Thanks,
Fabrice

>
>> + };
>> +
>> + counter {
>> + compatible = "st,stm32-lptimer-counter";
>> + pinctrl-names = "default";
>> + pinctrl-0 = <&lptim1_in_pins>;
>> + };
>> + };
>> --
>> 1.9.1
>>

2017-06-27 09:05:21

by Fabrice Gasnier

[permalink] [raw]
Subject: Re: [PATCH v2 5/8] dt-bindings: iio: Add STM32 LPTimer trigger binding

On 06/26/2017 10:38 PM, Jonathan Cameron wrote:
>
>
> On 26 June 2017 19:14:16 BST, Rob Herring <[email protected]> wrote:
>> On Wed, Jun 21, 2017 at 04:30:12PM +0200, Fabrice Gasnier wrote:
>>> Add documentation for STMicroelectronics STM32 Low-Power Timer
>> Trigger
>>> binding.
>>>
>>> Signed-off-by: Fabrice Gasnier <[email protected]>
>>> ---
>>> Changes in v2:
>>> - s/Low Power/Low-Power
>>> - remove leading 0x in example (parent node)
>>> - improve reg property description
>>> ---
>>> .../bindings/iio/timer/stm32-lptimer-trigger.txt | 23
>> ++++++++++++++++++++++
>>> 1 file changed, 23 insertions(+)
>>> create mode 100644
>> Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt
>>>
>>> diff --git
>> a/Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt
>> b/Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt
>>> new file mode 100644
>>> index 0000000..466d99f
>>> --- /dev/null
>>> +++
>> b/Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt
>>> @@ -0,0 +1,23 @@
>>> +STMicroelectronics STM32 Low-Power Timer Trigger
>>> +
>>> +STM32 Low-Power Timer provides trigger source (LPTIM output) that
>> can be used
>>> +by STM32 internal ADC and/or DAC.
>>> +
>>> +Must be a sub-node of an STM32 Low-Power Timer device tree node.
>>> +See ../mfd/stm32-lptimer.txt for details about the parent node.
>>> +
>>> +Required properties:
>>> +- compatible: Must be "st,stm32-lptimer-trigger".
>>> +- reg: Selects trigger hardware block. Must be 0, 1 or 2
>>> + respectively for lptimer1, lptimer2 or lptimer3
>>> + trigger output.
>>
>> I guess this answers my question. However, this seems like abuse of the
>>
>> reg prop. This should not be how you select a trigger. The DT should
>> describe all the h/w blocks and then the adc connection is a separate
>> property.
> I think this is miss described. Reg is labelling instances of the hardware block which is providing the trigger.

Hi Rob, Jonathan,

yes, reg identifies trigger hardware block.
I'll better describe in v3:
- reg: Identify trigger hardware block.

Best regards,
Fabrice

>
> The connection to an ADC etc is done from userspace.
>
>
>>
>> Rob
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to [email protected]
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>

2017-06-28 16:44:14

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v2 1/8] dt-bindings: mfd: Add STM32 LPTimer binding

On Tue, Jun 27, 2017 at 10:57:32AM +0200, Fabrice Gasnier wrote:
> On 06/26/2017 08:07 PM, Rob Herring wrote:
> > On Wed, Jun 21, 2017 at 04:30:08PM +0200, Fabrice Gasnier wrote:
> >> Add documentation for STMicroelectronics STM32 Low-Power Timer binding.
> >>
> >> Signed-off-by: Fabrice Gasnier <[email protected]>
> >> ---
> >> Changes in v2:
> >> - Lee's comments: s/Low Power/Low-Power/, remove 0x in example, improve
> >> properties descriptions
> >> ---
> >> .../devicetree/bindings/mfd/stm32-lptimer.txt | 48 ++++++++++++++++++++++
> >> 1 file changed, 48 insertions(+)
> >> create mode 100644 Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
> >>
> >> diff --git a/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt b/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
> >> new file mode 100644
> >> index 0000000..af859c8
> >> --- /dev/null
> >> +++ b/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
> >> @@ -0,0 +1,48 @@
> >> +STMicroelectronics STM32 Low-Power Timer
> >> +
> >> +The STM32 Low-Power Timer (LPTIM) is a 16-bit timer that provides several
> >> +functions:
> >> +- PWM output (with programmable prescaler, configurable polarity)
> >> +- Quadrature encoder, counter
> >> +- Trigger source for STM32 ADC/DAC (LPTIM_OUT)
> >> +
> >> +Required properties:
> >> +- compatible: Must be "st,stm32-lptimer".
> >> +- reg: Offset and length of the device's register set.
> >> +- clocks: Phandle to the clock used by the LP Timer module.
> >> +- clock-names: Must be "mux".
> >> +- #address-cells: Should be '<1>'.
> >> +- #size-cells: Should be '<0>'.
> >> +
> >> +Optional subnodes:
> >> +- pwm: See ../pwm/pwm-stm32-lp.txt
> >> +- counter: See ../iio/timer/stm32-lptimer-cnt.txt
> >> +- trigger: See ../iio/timer/stm32-lptimer-trigger.txt
> >> +
> >> +Example:
> >> +
> >> + lptimer1: lptimer@40002400 {
> >
> > timer@...
>
> Hi Rob,
>
> I initially put "lptimer" here to distinguish Low-Power Timer hardware
> from other "timers" hardware that can be found also on stm32 devices.
> I'd prefer to keep it, is it sensible from your point of view ?
> Please advise.

Node names are supposed to be the class of device (e.g. ethernet,
serial, etc.), not the specific device. The compatible is what
distinguishes it from other timers.

> >> + compatible = "st,stm32-lptimer";
> >> + reg = <0x40002400 0x400>;
> >> + clocks = <&timer_clk>;
> >> + clock-names = "mux";
> >> + #address-cells = <1>;
> >> + #size-cells = <0>;
> >> +
> >> + pwm {
> >> + compatible = "st,stm32-pwm-lp";
> >> + pinctrl-names = "default";
> >> + pinctrl-0 = <&lppwm1_pins>;
> >> + };
> >> +
> >> + trigger@0 {
> >> + compatible = "st,stm32-lptimer-trigger";
> >> + reg = <0>;
> >
> > Is there more than 1?
>
> reg identifies trigger hardware block.

Okay, the trigger patch needs to be reworded.

Rob

2017-06-29 07:19:20

by Fabrice Gasnier

[permalink] [raw]
Subject: Re: [PATCH v2 1/8] dt-bindings: mfd: Add STM32 LPTimer binding

On 06/28/2017 06:44 PM, Rob Herring wrote:
> On Tue, Jun 27, 2017 at 10:57:32AM +0200, Fabrice Gasnier wrote:
>> On 06/26/2017 08:07 PM, Rob Herring wrote:
>>> On Wed, Jun 21, 2017 at 04:30:08PM +0200, Fabrice Gasnier wrote:
>>>> Add documentation for STMicroelectronics STM32 Low-Power Timer binding.
>>>>
>>>> Signed-off-by: Fabrice Gasnier <[email protected]>
>>>> ---
>>>> Changes in v2:
>>>> - Lee's comments: s/Low Power/Low-Power/, remove 0x in example, improve
>>>> properties descriptions
>>>> ---
>>>> .../devicetree/bindings/mfd/stm32-lptimer.txt | 48 ++++++++++++++++++++++
>>>> 1 file changed, 48 insertions(+)
>>>> create mode 100644 Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt b/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
>>>> new file mode 100644
>>>> index 0000000..af859c8
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
>>>> @@ -0,0 +1,48 @@
>>>> +STMicroelectronics STM32 Low-Power Timer
>>>> +
>>>> +The STM32 Low-Power Timer (LPTIM) is a 16-bit timer that provides several
>>>> +functions:
>>>> +- PWM output (with programmable prescaler, configurable polarity)
>>>> +- Quadrature encoder, counter
>>>> +- Trigger source for STM32 ADC/DAC (LPTIM_OUT)
>>>> +
>>>> +Required properties:
>>>> +- compatible: Must be "st,stm32-lptimer".
>>>> +- reg: Offset and length of the device's register set.
>>>> +- clocks: Phandle to the clock used by the LP Timer module.
>>>> +- clock-names: Must be "mux".
>>>> +- #address-cells: Should be '<1>'.
>>>> +- #size-cells: Should be '<0>'.
>>>> +
>>>> +Optional subnodes:
>>>> +- pwm: See ../pwm/pwm-stm32-lp.txt
>>>> +- counter: See ../iio/timer/stm32-lptimer-cnt.txt
>>>> +- trigger: See ../iio/timer/stm32-lptimer-trigger.txt
>>>> +
>>>> +Example:
>>>> +
>>>> + lptimer1: lptimer@40002400 {
>>>
>>> timer@...
>>
>> Hi Rob,
>>
>> I initially put "lptimer" here to distinguish Low-Power Timer hardware
>> from other "timers" hardware that can be found also on stm32 devices.
>> I'd prefer to keep it, is it sensible from your point of view ?
>> Please advise.
>
> Node names are supposed to be the class of device (e.g. ethernet,
> serial, etc.), not the specific device. The compatible is what
> distinguishes it from other timers.

Hi Rob,

Thanks for your answer. I'll update this in v3.

>
>>>> + compatible = "st,stm32-lptimer";
>>>> + reg = <0x40002400 0x400>;
>>>> + clocks = <&timer_clk>;
>>>> + clock-names = "mux";
>>>> + #address-cells = <1>;
>>>> + #size-cells = <0>;
>>>> +
>>>> + pwm {
>>>> + compatible = "st,stm32-pwm-lp";
>>>> + pinctrl-names = "default";
>>>> + pinctrl-0 = <&lppwm1_pins>;
>>>> + };
>>>> +
>>>> + trigger@0 {
>>>> + compatible = "st,stm32-lptimer-trigger";
>>>> + reg = <0>;
>>>
>>> Is there more than 1?
>>
>> reg identifies trigger hardware block.
>
> Okay, the trigger patch needs to be reworded.
>
I'll update this in v3.

Best Regards,
Fabrice
> Rob
>

2017-06-30 13:57:22

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v2 6/8] iio: trigger: Add STM32 LPTimer trigger driver

On Mon, 26 Jun 2017 18:41:36 +0200
Fabrice Gasnier <[email protected]> wrote:

> On 06/24/2017 10:13 PM, Jonathan Cameron wrote:
> > On Wed, 21 Jun 2017 16:30:13 +0200
> > Fabrice Gasnier <[email protected]> wrote:
> >
> >> Add support for LPTIMx_OUT triggers that can be found on some STM32
> >> devices. These triggers can be used then by ADC or DAC.
> >> Typical usage is to configure LPTimer as PWM output (via pwm-stm32-lp)
> >> and have synchronised analog conversions with these triggers.
> >>
> >> Signed-off-by: Fabrice Gasnier <[email protected]>
> > Given this can't be used as a trigger for other devices (no exposed
> > interrupt?) I'd expect to see a validate_device callback provided for
> > the trigger ops. That would prevent other devices trying to use it.
>
> Hi Jonathan,
>
> This is something I had in mind also earlier. Only thing is...
> Basically, this is limiting: when trigger poll happens on device side
> (e.g. ADC), another device could use same trigger. But I admit this
> looks like corner case.
>
> I'll add it in next version, with additional patch for ADC part to
> validate it's a valid device (No DAC yet).
> I think I'll use INDIO_HARDWARE_TRIGGERED mode:
> - in adc driver: indio_dev->modes |= INDIO_HARDWARE_TRIGGERED;
> - in lptimer: if (indio_dev->modes & INDIO_HARDWARE_TRIGGERED)...
That may not be enough in of itself. Could be other hardware
triggered elements on the platform (quite likely with these
stm parts!)

J
>
> >
> > Otherwise, looks good.
>
> Many thanks for your review.
> Best Regards,
> Fabrice
>
> >
> > Jonathan
> >> ---
> >> Changes in v2:
> >> - s/Low Power/Low-Power
> >> - update few comments
> >> ---
> >> drivers/iio/trigger/Kconfig | 11 +++
> >> drivers/iio/trigger/Makefile | 1 +
> >> drivers/iio/trigger/stm32-lptimer-trigger.c | 110 ++++++++++++++++++++++++++
> >> include/linux/iio/timer/stm32-lptim-trigger.h | 24 ++++++
> >> 4 files changed, 146 insertions(+)
> >> create mode 100644 drivers/iio/trigger/stm32-lptimer-trigger.c
> >> create mode 100644 include/linux/iio/timer/stm32-lptim-trigger.h
> >>
> >> diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig
> >> index e4d4e63..a633d2c 100644
> >> --- a/drivers/iio/trigger/Kconfig
> >> +++ b/drivers/iio/trigger/Kconfig
> >> @@ -24,6 +24,17 @@ config IIO_INTERRUPT_TRIGGER
> >> To compile this driver as a module, choose M here: the
> >> module will be called iio-trig-interrupt.
> >>
> >> +config IIO_STM32_LPTIMER_TRIGGER
> >> + tristate "STM32 Low-Power Timer Trigger"
> >> + depends on MFD_STM32_LPTIMER || COMPILE_TEST
> >> + help
> >> + Select this option to enable STM32 Low-Power Timer Trigger.
> >> + This can be used as trigger source for STM32 internal ADC
> >> + and/or DAC.
> >> +
> >> + To compile this driver as a module, choose M here: the
> >> + module will be called stm32-lptimer-trigger.
> >> +
> >> config IIO_STM32_TIMER_TRIGGER
> >> tristate "STM32 Timer Trigger"
> >> depends on (ARCH_STM32 && OF && MFD_STM32_TIMERS) || COMPILE_TEST
> >> diff --git a/drivers/iio/trigger/Makefile b/drivers/iio/trigger/Makefile
> >> index 5c4ecd3..0a72a2a 100644
> >> --- a/drivers/iio/trigger/Makefile
> >> +++ b/drivers/iio/trigger/Makefile
> >> @@ -6,6 +6,7 @@
> >>
> >> obj-$(CONFIG_IIO_HRTIMER_TRIGGER) += iio-trig-hrtimer.o
> >> obj-$(CONFIG_IIO_INTERRUPT_TRIGGER) += iio-trig-interrupt.o
> >> +obj-$(CONFIG_IIO_STM32_LPTIMER_TRIGGER) += stm32-lptimer-trigger.o
> >> obj-$(CONFIG_IIO_STM32_TIMER_TRIGGER) += stm32-timer-trigger.o
> >> obj-$(CONFIG_IIO_SYSFS_TRIGGER) += iio-trig-sysfs.o
> >> obj-$(CONFIG_IIO_TIGHTLOOP_TRIGGER) += iio-trig-loop.o
> >> diff --git a/drivers/iio/trigger/stm32-lptimer-trigger.c b/drivers/iio/trigger/stm32-lptimer-trigger.c
> >> new file mode 100644
> >> index 0000000..bcb9aa2
> >> --- /dev/null
> >> +++ b/drivers/iio/trigger/stm32-lptimer-trigger.c
> >> @@ -0,0 +1,110 @@
> >> +/*
> >> + * STM32 Low-Power Timer Trigger driver
> >> + *
> >> + * Copyright (C) STMicroelectronics 2017
> >> + *
> >> + * Author: Fabrice Gasnier <[email protected]>.
> >> + *
> >> + * License terms: GNU General Public License (GPL), version 2
> >> + *
> >> + * Inspired by Benjamin Gaignard's stm32-timer-trigger driver
> >> + */
> >> +
> >> +#include <linux/iio/iio.h>
> >> +#include <linux/iio/timer/stm32-lptim-trigger.h>
> >> +#include <linux/iio/trigger.h>
> >> +#include <linux/mfd/stm32-lptimer.h>
> >> +#include <linux/module.h>
> >> +#include <linux/platform_device.h>
> >> +
> >> +/* List Low-Power Timer triggers */
> >> +static const char * const stm32_lptim_triggers[] = {
> >> + LPTIM1_OUT,
> >> + LPTIM2_OUT,
> >> + LPTIM3_OUT,
> >> +};
> >> +
> >> +struct stm32_lptim_trigger {
> >> + struct device *dev;
> >> + const char *trg;
> >> +};
> >> +
> >> +static const struct iio_trigger_ops stm32_lptim_trigger_ops = {
> >> + .owner = THIS_MODULE,
> >> +};
> >> +
> >> +/**
> >> + * is_stm32_lptim_trigger
> >> + * @trig: trigger to be checked
> >> + *
> >> + * return true if the trigger is a valid STM32 IIO Low-Power Timer Trigger
> >> + * either return false
> >> + */
> >> +bool is_stm32_lptim_trigger(struct iio_trigger *trig)
> >> +{
> >> + return (trig->ops == &stm32_lptim_trigger_ops);
> >> +}
> >> +EXPORT_SYMBOL(is_stm32_lptim_trigger);
> >> +
> >> +static int stm32_lptim_setup_trig(struct stm32_lptim_trigger *priv)
> >> +{
> >> + struct iio_trigger *trig;
> >> +
> >> + trig = devm_iio_trigger_alloc(priv->dev, "%s", priv->trg);
> >> + if (!trig)
> >> + return -ENOMEM;
> >> +
> >> + trig->dev.parent = priv->dev->parent;
> >> + trig->ops = &stm32_lptim_trigger_ops;
> >> + iio_trigger_set_drvdata(trig, priv);
> >> +
> >> + return devm_iio_trigger_register(priv->dev, trig);
> >> +}
> >> +
> >> +static int stm32_lptim_trigger_probe(struct platform_device *pdev)
> >> +{
> >> + struct stm32_lptim_trigger *priv;
> >> + u32 index;
> >> + int ret;
> >> +
> >> + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> >> + if (!priv)
> >> + return -ENOMEM;
> >> +
> >> + if (of_property_read_u32(pdev->dev.of_node, "reg", &index))
> >> + return -EINVAL;
> >> +
> >> + if (index >= ARRAY_SIZE(stm32_lptim_triggers))
> >> + return -EINVAL;
> >> +
> >> + priv->dev = &pdev->dev;
> >> + priv->trg = stm32_lptim_triggers[index];
> >> +
> >> + ret = stm32_lptim_setup_trig(priv);
> >> + if (ret)
> >> + return ret;
> >> +
> >> + platform_set_drvdata(pdev, priv);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static const struct of_device_id stm32_lptim_trig_of_match[] = {
> >> + { .compatible = "st,stm32-lptimer-trigger", },
> >> + {},
> >> +};
> >> +MODULE_DEVICE_TABLE(of, stm32_lptim_trig_of_match);
> >> +
> >> +static struct platform_driver stm32_lptim_trigger_driver = {
> >> + .probe = stm32_lptim_trigger_probe,
> >> + .driver = {
> >> + .name = "stm32-lptimer-trigger",
> >> + .of_match_table = stm32_lptim_trig_of_match,
> >> + },
> >> +};
> >> +module_platform_driver(stm32_lptim_trigger_driver);
> >> +
> >> +MODULE_AUTHOR("Fabrice Gasnier <[email protected]>");
> >> +MODULE_ALIAS("platform:stm32-lptimer-trigger");
> >> +MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM trigger driver");
> >> +MODULE_LICENSE("GPL v2");
> >> diff --git a/include/linux/iio/timer/stm32-lptim-trigger.h b/include/linux/iio/timer/stm32-lptim-trigger.h
> >> new file mode 100644
> >> index 0000000..cb795b1
> >> --- /dev/null
> >> +++ b/include/linux/iio/timer/stm32-lptim-trigger.h
> >> @@ -0,0 +1,24 @@
> >> +/*
> >> + * Copyright (C) STMicroelectronics 2017
> >> + *
> >> + * Author: Fabrice Gasnier <[email protected]>
> >> + *
> >> + * License terms: GNU General Public License (GPL), version 2
> >> + */
> >> +
> >> +#ifndef _STM32_LPTIM_TRIGGER_H_
> >> +#define _STM32_LPTIM_TRIGGER_H_
> >> +
> >> +#define LPTIM1_OUT "lptim1_out"
> >> +#define LPTIM2_OUT "lptim2_out"
> >> +#define LPTIM3_OUT "lptim3_out"
> >> +
> >> +#if IS_ENABLED(CONFIG_IIO_STM32_LPTIMER_TRIGGER)
> >> +bool is_stm32_lptim_trigger(struct iio_trigger *trig);
> >> +#else
> >> +static inline bool is_stm32_lptim_trigger(struct iio_trigger *trig)
> >> +{
> >> + return false;
> >> +}
> >> +#endif
> >> +#endif
> >

2017-06-30 16:27:35

by Fabrice Gasnier

[permalink] [raw]
Subject: Re: [PATCH v2 6/8] iio: trigger: Add STM32 LPTimer trigger driver

On 06/30/2017 03:57 PM, Jonathan Cameron wrote:
> On Mon, 26 Jun 2017 18:41:36 +0200
> Fabrice Gasnier <[email protected]> wrote:
>
>> On 06/24/2017 10:13 PM, Jonathan Cameron wrote:
>>> On Wed, 21 Jun 2017 16:30:13 +0200
>>> Fabrice Gasnier <[email protected]> wrote:
>>>
>>>> Add support for LPTIMx_OUT triggers that can be found on some STM32
>>>> devices. These triggers can be used then by ADC or DAC.
>>>> Typical usage is to configure LPTimer as PWM output (via pwm-stm32-lp)
>>>> and have synchronised analog conversions with these triggers.
>>>>
>>>> Signed-off-by: Fabrice Gasnier <[email protected]>
>>> Given this can't be used as a trigger for other devices (no exposed
>>> interrupt?) I'd expect to see a validate_device callback provided for
>>> the trigger ops. That would prevent other devices trying to use it.
>>
>> Hi Jonathan,
>>
>> This is something I had in mind also earlier. Only thing is...
>> Basically, this is limiting: when trigger poll happens on device side
>> (e.g. ADC), another device could use same trigger. But I admit this
>> looks like corner case.
>>
>> I'll add it in next version, with additional patch for ADC part to
>> validate it's a valid device (No DAC yet).
>> I think I'll use INDIO_HARDWARE_TRIGGERED mode:
>> - in adc driver: indio_dev->modes |= INDIO_HARDWARE_TRIGGERED;
>> - in lptimer: if (indio_dev->modes & INDIO_HARDWARE_TRIGGERED)...
> That may not be enough in of itself. Could be other hardware
> triggered elements on the platform (quite likely with these
> stm parts!)
>

Hi Jonathan,

As far as I can see, devices supporting it need to check trigger anyway
(e.g. validate_trigger() cb). That is the case currently on stm32-adc
driver. Do you think this "handshake" is okay ?

If not, I can probably add an export symbol instead, in stm32-adc, like:
bool is_stm32_adc_iio_dev(struct iio_dev *indio_dev);
Then use it in validate_device callback.

Please let me know.

Best regards,
Fabrice

> J
>>
>>>
>>> Otherwise, looks good.
>>
>> Many thanks for your review.
>> Best Regards,
>> Fabrice
>>
>>>
>>> Jonathan
>>>> ---
>>>> Changes in v2:
>>>> - s/Low Power/Low-Power
>>>> - update few comments
>>>> ---
>>>> drivers/iio/trigger/Kconfig | 11 +++
>>>> drivers/iio/trigger/Makefile | 1 +
>>>> drivers/iio/trigger/stm32-lptimer-trigger.c | 110 ++++++++++++++++++++++++++
>>>> include/linux/iio/timer/stm32-lptim-trigger.h | 24 ++++++
>>>> 4 files changed, 146 insertions(+)
>>>> create mode 100644 drivers/iio/trigger/stm32-lptimer-trigger.c
>>>> create mode 100644 include/linux/iio/timer/stm32-lptim-trigger.h
>>>>
>>>> diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig
>>>> index e4d4e63..a633d2c 100644
>>>> --- a/drivers/iio/trigger/Kconfig
>>>> +++ b/drivers/iio/trigger/Kconfig
>>>> @@ -24,6 +24,17 @@ config IIO_INTERRUPT_TRIGGER
>>>> To compile this driver as a module, choose M here: the
>>>> module will be called iio-trig-interrupt.
>>>>
>>>> +config IIO_STM32_LPTIMER_TRIGGER
>>>> + tristate "STM32 Low-Power Timer Trigger"
>>>> + depends on MFD_STM32_LPTIMER || COMPILE_TEST
>>>> + help
>>>> + Select this option to enable STM32 Low-Power Timer Trigger.
>>>> + This can be used as trigger source for STM32 internal ADC
>>>> + and/or DAC.
>>>> +
>>>> + To compile this driver as a module, choose M here: the
>>>> + module will be called stm32-lptimer-trigger.
>>>> +
>>>> config IIO_STM32_TIMER_TRIGGER
>>>> tristate "STM32 Timer Trigger"
>>>> depends on (ARCH_STM32 && OF && MFD_STM32_TIMERS) || COMPILE_TEST
>>>> diff --git a/drivers/iio/trigger/Makefile b/drivers/iio/trigger/Makefile
>>>> index 5c4ecd3..0a72a2a 100644
>>>> --- a/drivers/iio/trigger/Makefile
>>>> +++ b/drivers/iio/trigger/Makefile
>>>> @@ -6,6 +6,7 @@
>>>>
>>>> obj-$(CONFIG_IIO_HRTIMER_TRIGGER) += iio-trig-hrtimer.o
>>>> obj-$(CONFIG_IIO_INTERRUPT_TRIGGER) += iio-trig-interrupt.o
>>>> +obj-$(CONFIG_IIO_STM32_LPTIMER_TRIGGER) += stm32-lptimer-trigger.o
>>>> obj-$(CONFIG_IIO_STM32_TIMER_TRIGGER) += stm32-timer-trigger.o
>>>> obj-$(CONFIG_IIO_SYSFS_TRIGGER) += iio-trig-sysfs.o
>>>> obj-$(CONFIG_IIO_TIGHTLOOP_TRIGGER) += iio-trig-loop.o
>>>> diff --git a/drivers/iio/trigger/stm32-lptimer-trigger.c b/drivers/iio/trigger/stm32-lptimer-trigger.c
>>>> new file mode 100644
>>>> index 0000000..bcb9aa2
>>>> --- /dev/null
>>>> +++ b/drivers/iio/trigger/stm32-lptimer-trigger.c
>>>> @@ -0,0 +1,110 @@
>>>> +/*
>>>> + * STM32 Low-Power Timer Trigger driver
>>>> + *
>>>> + * Copyright (C) STMicroelectronics 2017
>>>> + *
>>>> + * Author: Fabrice Gasnier <[email protected]>.
>>>> + *
>>>> + * License terms: GNU General Public License (GPL), version 2
>>>> + *
>>>> + * Inspired by Benjamin Gaignard's stm32-timer-trigger driver
>>>> + */
>>>> +
>>>> +#include <linux/iio/iio.h>
>>>> +#include <linux/iio/timer/stm32-lptim-trigger.h>
>>>> +#include <linux/iio/trigger.h>
>>>> +#include <linux/mfd/stm32-lptimer.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/platform_device.h>
>>>> +
>>>> +/* List Low-Power Timer triggers */
>>>> +static const char * const stm32_lptim_triggers[] = {
>>>> + LPTIM1_OUT,
>>>> + LPTIM2_OUT,
>>>> + LPTIM3_OUT,
>>>> +};
>>>> +
>>>> +struct stm32_lptim_trigger {
>>>> + struct device *dev;
>>>> + const char *trg;
>>>> +};
>>>> +
>>>> +static const struct iio_trigger_ops stm32_lptim_trigger_ops = {
>>>> + .owner = THIS_MODULE,
>>>> +};
>>>> +
>>>> +/**
>>>> + * is_stm32_lptim_trigger
>>>> + * @trig: trigger to be checked
>>>> + *
>>>> + * return true if the trigger is a valid STM32 IIO Low-Power Timer Trigger
>>>> + * either return false
>>>> + */
>>>> +bool is_stm32_lptim_trigger(struct iio_trigger *trig)
>>>> +{
>>>> + return (trig->ops == &stm32_lptim_trigger_ops);
>>>> +}
>>>> +EXPORT_SYMBOL(is_stm32_lptim_trigger);
>>>> +
>>>> +static int stm32_lptim_setup_trig(struct stm32_lptim_trigger *priv)
>>>> +{
>>>> + struct iio_trigger *trig;
>>>> +
>>>> + trig = devm_iio_trigger_alloc(priv->dev, "%s", priv->trg);
>>>> + if (!trig)
>>>> + return -ENOMEM;
>>>> +
>>>> + trig->dev.parent = priv->dev->parent;
>>>> + trig->ops = &stm32_lptim_trigger_ops;
>>>> + iio_trigger_set_drvdata(trig, priv);
>>>> +
>>>> + return devm_iio_trigger_register(priv->dev, trig);
>>>> +}
>>>> +
>>>> +static int stm32_lptim_trigger_probe(struct platform_device *pdev)
>>>> +{
>>>> + struct stm32_lptim_trigger *priv;
>>>> + u32 index;
>>>> + int ret;
>>>> +
>>>> + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
>>>> + if (!priv)
>>>> + return -ENOMEM;
>>>> +
>>>> + if (of_property_read_u32(pdev->dev.of_node, "reg", &index))
>>>> + return -EINVAL;
>>>> +
>>>> + if (index >= ARRAY_SIZE(stm32_lptim_triggers))
>>>> + return -EINVAL;
>>>> +
>>>> + priv->dev = &pdev->dev;
>>>> + priv->trg = stm32_lptim_triggers[index];
>>>> +
>>>> + ret = stm32_lptim_setup_trig(priv);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + platform_set_drvdata(pdev, priv);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static const struct of_device_id stm32_lptim_trig_of_match[] = {
>>>> + { .compatible = "st,stm32-lptimer-trigger", },
>>>> + {},
>>>> +};
>>>> +MODULE_DEVICE_TABLE(of, stm32_lptim_trig_of_match);
>>>> +
>>>> +static struct platform_driver stm32_lptim_trigger_driver = {
>>>> + .probe = stm32_lptim_trigger_probe,
>>>> + .driver = {
>>>> + .name = "stm32-lptimer-trigger",
>>>> + .of_match_table = stm32_lptim_trig_of_match,
>>>> + },
>>>> +};
>>>> +module_platform_driver(stm32_lptim_trigger_driver);
>>>> +
>>>> +MODULE_AUTHOR("Fabrice Gasnier <[email protected]>");
>>>> +MODULE_ALIAS("platform:stm32-lptimer-trigger");
>>>> +MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM trigger driver");
>>>> +MODULE_LICENSE("GPL v2");
>>>> diff --git a/include/linux/iio/timer/stm32-lptim-trigger.h b/include/linux/iio/timer/stm32-lptim-trigger.h
>>>> new file mode 100644
>>>> index 0000000..cb795b1
>>>> --- /dev/null
>>>> +++ b/include/linux/iio/timer/stm32-lptim-trigger.h
>>>> @@ -0,0 +1,24 @@
>>>> +/*
>>>> + * Copyright (C) STMicroelectronics 2017
>>>> + *
>>>> + * Author: Fabrice Gasnier <[email protected]>
>>>> + *
>>>> + * License terms: GNU General Public License (GPL), version 2
>>>> + */
>>>> +
>>>> +#ifndef _STM32_LPTIM_TRIGGER_H_
>>>> +#define _STM32_LPTIM_TRIGGER_H_
>>>> +
>>>> +#define LPTIM1_OUT "lptim1_out"
>>>> +#define LPTIM2_OUT "lptim2_out"
>>>> +#define LPTIM3_OUT "lptim3_out"
>>>> +
>>>> +#if IS_ENABLED(CONFIG_IIO_STM32_LPTIMER_TRIGGER)
>>>> +bool is_stm32_lptim_trigger(struct iio_trigger *trig);
>>>> +#else
>>>> +static inline bool is_stm32_lptim_trigger(struct iio_trigger *trig)
>>>> +{
>>>> + return false;
>>>> +}
>>>> +#endif
>>>> +#endif
>>>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>

2017-06-30 18:20:07

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v2 8/8] iio: counter: Add support for STM32 LPTimer

On Tue, 27 Jun 2017 10:21:43 +0200
Benjamin Gaignard <[email protected]> wrote:

> 2017-06-26 22:29 GMT+02:00 William Breathitt Gray <[email protected]>:
> > On Sat, Jun 24, 2017 at 09:35:39PM +0100, Jonathan Cameron wrote:
> >>On Wed, 21 Jun 2017 16:30:15 +0200
> >>Fabrice Gasnier <[email protected]> wrote:
> >>
> >>> Add support for STM32 Low-Power Timer, that can be used as counter
> >>> or quadrature encoder.
> >>>
> >>> Signed-off-by: Fabrice Gasnier <[email protected]>
> >>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: [email protected]
> >>> +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: [email protected]
> >>> +Description:
> >>> + Reading returns the list possible quadrature modes.
> >>> +
> >>> +What: /sys/bus/iio/devices/iio:deviceX/in_count0_quadrature_mode
> >>> +KernelVersion: 4.13
> >>> +Contact: [email protected]
> >>> +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: [email protected]
> >>> +Description:
> >>> + Reading returns the list possible active edges.
> >>> +
> >>> +What: /sys/bus/iio/devices/iio:deviceX/in_count0_polarity
> >>> +KernelVersion: 4.13
> >>> +Contact: [email protected]
> >>> +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.
As I read this, the complexities of quadrature counting aren't covered...
The reason it works and can tell direction is down to order of state
transitions across the two channels. So how do we describe that? It's not
even the case that a particular existing state is relevant, there is
a temporal element (basically the state machine).
http://www.edn.com/design/integrated-circuit-design/4363949/Decode-a-quadrature-encoder-in-software
is a good description of the algorithm...

Now if we had some concept of composite signal lines, so a quadrature
pair was treated as one entity then that might work - but we still aren't
dealing with edges as such, but rather the state machine change
they represent.

Agreed the counter interface shouldn't be particularly tied to
quadrature type signals, but those are one of the most common
types so we do need to make sure we handle them cleanly.

All the other common counter types are more straight forward.
> >
> > 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 <[email protected]>
> >>> + *
> >>> + * 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 <[email protected]>");
> >>> +MODULE_ALIAS("platform:stm32-lptimer-counter");
> >>> +MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
> >>> +MODULE_LICENSE("GPL v2");
> >>
>
>
>

2017-06-30 18:29:06

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v2 6/8] iio: trigger: Add STM32 LPTimer trigger driver

On Fri, 30 Jun 2017 18:26:56 +0200
Fabrice Gasnier <[email protected]> wrote:

> On 06/30/2017 03:57 PM, Jonathan Cameron wrote:
> > On Mon, 26 Jun 2017 18:41:36 +0200
> > Fabrice Gasnier <[email protected]> wrote:
> >
> >> On 06/24/2017 10:13 PM, Jonathan Cameron wrote:
> >>> On Wed, 21 Jun 2017 16:30:13 +0200
> >>> Fabrice Gasnier <[email protected]> wrote:
> >>>
> >>>> Add support for LPTIMx_OUT triggers that can be found on some STM32
> >>>> devices. These triggers can be used then by ADC or DAC.
> >>>> Typical usage is to configure LPTimer as PWM output (via pwm-stm32-lp)
> >>>> and have synchronised analog conversions with these triggers.
> >>>>
> >>>> Signed-off-by: Fabrice Gasnier <[email protected]>
> >>> Given this can't be used as a trigger for other devices (no exposed
> >>> interrupt?) I'd expect to see a validate_device callback provided for
> >>> the trigger ops. That would prevent other devices trying to use it.
> >>
> >> Hi Jonathan,
> >>
> >> This is something I had in mind also earlier. Only thing is...
> >> Basically, this is limiting: when trigger poll happens on device side
> >> (e.g. ADC), another device could use same trigger. But I admit this
> >> looks like corner case.
> >>
> >> I'll add it in next version, with additional patch for ADC part to
> >> validate it's a valid device (No DAC yet).
> >> I think I'll use INDIO_HARDWARE_TRIGGERED mode:
> >> - in adc driver: indio_dev->modes |= INDIO_HARDWARE_TRIGGERED;
> >> - in lptimer: if (indio_dev->modes & INDIO_HARDWARE_TRIGGERED)...
> > That may not be enough in of itself. Could be other hardware
> > triggered elements on the platform (quite likely with these
> > stm parts!)
> >
>
> Hi Jonathan,
>
> As far as I can see, devices supporting it need to check trigger anyway
> (e.g. validate_trigger() cb). That is the case currently on stm32-adc
> driver. Do you think this "handshake" is okay ?
>
> If not, I can probably add an export symbol instead, in stm32-adc, like:
> bool is_stm32_adc_iio_dev(struct iio_dev *indio_dev);
> Then use it in validate_device callback.
Anything is fine as long as it guarantees that we can't bind a device to
a trigger it can't use, or the other way around. You have check from
both sides to prevent other devices using the trigger, or other triggers
being used by the device. As far as I can see these are tightly coupled.
If there weren't several of them to chose from I'd argue we should drop
the fact the trigger is exposed at all (like we do in quite a few
devices with a fifo).

J
>
> Please let me know.
>
> Best regards,
> Fabrice
>
> > J
> >>
> >>>
> >>> Otherwise, looks good.
> >>
> >> Many thanks for your review.
> >> Best Regards,
> >> Fabrice
> >>
> >>>
> >>> Jonathan
> >>>> ---
> >>>> Changes in v2:
> >>>> - s/Low Power/Low-Power
> >>>> - update few comments
> >>>> ---
> >>>> drivers/iio/trigger/Kconfig | 11 +++
> >>>> drivers/iio/trigger/Makefile | 1 +
> >>>> drivers/iio/trigger/stm32-lptimer-trigger.c | 110 ++++++++++++++++++++++++++
> >>>> include/linux/iio/timer/stm32-lptim-trigger.h | 24 ++++++
> >>>> 4 files changed, 146 insertions(+)
> >>>> create mode 100644 drivers/iio/trigger/stm32-lptimer-trigger.c
> >>>> create mode 100644 include/linux/iio/timer/stm32-lptim-trigger.h
> >>>>
> >>>> diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig
> >>>> index e4d4e63..a633d2c 100644
> >>>> --- a/drivers/iio/trigger/Kconfig
> >>>> +++ b/drivers/iio/trigger/Kconfig
> >>>> @@ -24,6 +24,17 @@ config IIO_INTERRUPT_TRIGGER
> >>>> To compile this driver as a module, choose M here: the
> >>>> module will be called iio-trig-interrupt.
> >>>>
> >>>> +config IIO_STM32_LPTIMER_TRIGGER
> >>>> + tristate "STM32 Low-Power Timer Trigger"
> >>>> + depends on MFD_STM32_LPTIMER || COMPILE_TEST
> >>>> + help
> >>>> + Select this option to enable STM32 Low-Power Timer Trigger.
> >>>> + This can be used as trigger source for STM32 internal ADC
> >>>> + and/or DAC.
> >>>> +
> >>>> + To compile this driver as a module, choose M here: the
> >>>> + module will be called stm32-lptimer-trigger.
> >>>> +
> >>>> config IIO_STM32_TIMER_TRIGGER
> >>>> tristate "STM32 Timer Trigger"
> >>>> depends on (ARCH_STM32 && OF && MFD_STM32_TIMERS) || COMPILE_TEST
> >>>> diff --git a/drivers/iio/trigger/Makefile b/drivers/iio/trigger/Makefile
> >>>> index 5c4ecd3..0a72a2a 100644
> >>>> --- a/drivers/iio/trigger/Makefile
> >>>> +++ b/drivers/iio/trigger/Makefile
> >>>> @@ -6,6 +6,7 @@
> >>>>
> >>>> obj-$(CONFIG_IIO_HRTIMER_TRIGGER) += iio-trig-hrtimer.o
> >>>> obj-$(CONFIG_IIO_INTERRUPT_TRIGGER) += iio-trig-interrupt.o
> >>>> +obj-$(CONFIG_IIO_STM32_LPTIMER_TRIGGER) += stm32-lptimer-trigger.o
> >>>> obj-$(CONFIG_IIO_STM32_TIMER_TRIGGER) += stm32-timer-trigger.o
> >>>> obj-$(CONFIG_IIO_SYSFS_TRIGGER) += iio-trig-sysfs.o
> >>>> obj-$(CONFIG_IIO_TIGHTLOOP_TRIGGER) += iio-trig-loop.o
> >>>> diff --git a/drivers/iio/trigger/stm32-lptimer-trigger.c b/drivers/iio/trigger/stm32-lptimer-trigger.c
> >>>> new file mode 100644
> >>>> index 0000000..bcb9aa2
> >>>> --- /dev/null
> >>>> +++ b/drivers/iio/trigger/stm32-lptimer-trigger.c
> >>>> @@ -0,0 +1,110 @@
> >>>> +/*
> >>>> + * STM32 Low-Power Timer Trigger driver
> >>>> + *
> >>>> + * Copyright (C) STMicroelectronics 2017
> >>>> + *
> >>>> + * Author: Fabrice Gasnier <[email protected]>.
> >>>> + *
> >>>> + * License terms: GNU General Public License (GPL), version 2
> >>>> + *
> >>>> + * Inspired by Benjamin Gaignard's stm32-timer-trigger driver
> >>>> + */
> >>>> +
> >>>> +#include <linux/iio/iio.h>
> >>>> +#include <linux/iio/timer/stm32-lptim-trigger.h>
> >>>> +#include <linux/iio/trigger.h>
> >>>> +#include <linux/mfd/stm32-lptimer.h>
> >>>> +#include <linux/module.h>
> >>>> +#include <linux/platform_device.h>
> >>>> +
> >>>> +/* List Low-Power Timer triggers */
> >>>> +static const char * const stm32_lptim_triggers[] = {
> >>>> + LPTIM1_OUT,
> >>>> + LPTIM2_OUT,
> >>>> + LPTIM3_OUT,
> >>>> +};
> >>>> +
> >>>> +struct stm32_lptim_trigger {
> >>>> + struct device *dev;
> >>>> + const char *trg;
> >>>> +};
> >>>> +
> >>>> +static const struct iio_trigger_ops stm32_lptim_trigger_ops = {
> >>>> + .owner = THIS_MODULE,
> >>>> +};
> >>>> +
> >>>> +/**
> >>>> + * is_stm32_lptim_trigger
> >>>> + * @trig: trigger to be checked
> >>>> + *
> >>>> + * return true if the trigger is a valid STM32 IIO Low-Power Timer Trigger
> >>>> + * either return false
> >>>> + */
> >>>> +bool is_stm32_lptim_trigger(struct iio_trigger *trig)
> >>>> +{
> >>>> + return (trig->ops == &stm32_lptim_trigger_ops);
> >>>> +}
> >>>> +EXPORT_SYMBOL(is_stm32_lptim_trigger);
> >>>> +
> >>>> +static int stm32_lptim_setup_trig(struct stm32_lptim_trigger *priv)
> >>>> +{
> >>>> + struct iio_trigger *trig;
> >>>> +
> >>>> + trig = devm_iio_trigger_alloc(priv->dev, "%s", priv->trg);
> >>>> + if (!trig)
> >>>> + return -ENOMEM;
> >>>> +
> >>>> + trig->dev.parent = priv->dev->parent;
> >>>> + trig->ops = &stm32_lptim_trigger_ops;
> >>>> + iio_trigger_set_drvdata(trig, priv);
> >>>> +
> >>>> + return devm_iio_trigger_register(priv->dev, trig);
> >>>> +}
> >>>> +
> >>>> +static int stm32_lptim_trigger_probe(struct platform_device *pdev)
> >>>> +{
> >>>> + struct stm32_lptim_trigger *priv;
> >>>> + u32 index;
> >>>> + int ret;
> >>>> +
> >>>> + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> >>>> + if (!priv)
> >>>> + return -ENOMEM;
> >>>> +
> >>>> + if (of_property_read_u32(pdev->dev.of_node, "reg", &index))
> >>>> + return -EINVAL;
> >>>> +
> >>>> + if (index >= ARRAY_SIZE(stm32_lptim_triggers))
> >>>> + return -EINVAL;
> >>>> +
> >>>> + priv->dev = &pdev->dev;
> >>>> + priv->trg = stm32_lptim_triggers[index];
> >>>> +
> >>>> + ret = stm32_lptim_setup_trig(priv);
> >>>> + if (ret)
> >>>> + return ret;
> >>>> +
> >>>> + platform_set_drvdata(pdev, priv);
> >>>> +
> >>>> + return 0;
> >>>> +}
> >>>> +
> >>>> +static const struct of_device_id stm32_lptim_trig_of_match[] = {
> >>>> + { .compatible = "st,stm32-lptimer-trigger", },
> >>>> + {},
> >>>> +};
> >>>> +MODULE_DEVICE_TABLE(of, stm32_lptim_trig_of_match);
> >>>> +
> >>>> +static struct platform_driver stm32_lptim_trigger_driver = {
> >>>> + .probe = stm32_lptim_trigger_probe,
> >>>> + .driver = {
> >>>> + .name = "stm32-lptimer-trigger",
> >>>> + .of_match_table = stm32_lptim_trig_of_match,
> >>>> + },
> >>>> +};
> >>>> +module_platform_driver(stm32_lptim_trigger_driver);
> >>>> +
> >>>> +MODULE_AUTHOR("Fabrice Gasnier <[email protected]>");
> >>>> +MODULE_ALIAS("platform:stm32-lptimer-trigger");
> >>>> +MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM trigger driver");
> >>>> +MODULE_LICENSE("GPL v2");
> >>>> diff --git a/include/linux/iio/timer/stm32-lptim-trigger.h b/include/linux/iio/timer/stm32-lptim-trigger.h
> >>>> new file mode 100644
> >>>> index 0000000..cb795b1
> >>>> --- /dev/null
> >>>> +++ b/include/linux/iio/timer/stm32-lptim-trigger.h
> >>>> @@ -0,0 +1,24 @@
> >>>> +/*
> >>>> + * Copyright (C) STMicroelectronics 2017
> >>>> + *
> >>>> + * Author: Fabrice Gasnier <[email protected]>
> >>>> + *
> >>>> + * License terms: GNU General Public License (GPL), version 2
> >>>> + */
> >>>> +
> >>>> +#ifndef _STM32_LPTIM_TRIGGER_H_
> >>>> +#define _STM32_LPTIM_TRIGGER_H_
> >>>> +
> >>>> +#define LPTIM1_OUT "lptim1_out"
> >>>> +#define LPTIM2_OUT "lptim2_out"
> >>>> +#define LPTIM3_OUT "lptim3_out"
> >>>> +
> >>>> +#if IS_ENABLED(CONFIG_IIO_STM32_LPTIMER_TRIGGER)
> >>>> +bool is_stm32_lptim_trigger(struct iio_trigger *trig);
> >>>> +#else
> >>>> +static inline bool is_stm32_lptim_trigger(struct iio_trigger *trig)
> >>>> +{
> >>>> + return false;
> >>>> +}
> >>>> +#endif
> >>>> +#endif
> >>>
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> > the body of a message to [email protected]
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
> >

2017-06-30 20:36:53

by Benjamin Gaignard

[permalink] [raw]
Subject: Re: [PATCH v2 8/8] iio: counter: Add support for STM32 LPTimer

2017-06-30 20:19 GMT+02:00 Jonathan Cameron <[email protected]>:
> On Tue, 27 Jun 2017 10:21:43 +0200
> Benjamin Gaignard <[email protected]> wrote:
>
>> 2017-06-26 22:29 GMT+02:00 William Breathitt Gray <[email protected]>:
>> > On Sat, Jun 24, 2017 at 09:35:39PM +0100, Jonathan Cameron wrote:
>> >>On Wed, 21 Jun 2017 16:30:15 +0200
>> >>Fabrice Gasnier <[email protected]> wrote:
>> >>
>> >>> Add support for STM32 Low-Power Timer, that can be used as counter
>> >>> or quadrature encoder.
>> >>>
>> >>> Signed-off-by: Fabrice Gasnier <[email protected]>
>> >>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: [email protected]
>> >>> +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: [email protected]
>> >>> +Description:
>> >>> + Reading returns the list possible quadrature modes.
>> >>> +
>> >>> +What: /sys/bus/iio/devices/iio:deviceX/in_count0_quadrature_mode
>> >>> +KernelVersion: 4.13
>> >>> +Contact: [email protected]
>> >>> +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: [email protected]
>> >>> +Description:
>> >>> + Reading returns the list possible active edges.
>> >>> +
>> >>> +What: /sys/bus/iio/devices/iio:deviceX/in_count0_polarity
>> >>> +KernelVersion: 4.13
>> >>> +Contact: [email protected]
>> >>> +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.
> As I read this, the complexities of quadrature counting aren't covered...
> The reason it works and can tell direction is down to order of state
> transitions across the two channels. So how do we describe that? It's not
> even the case that a particular existing state is relevant, there is
> a temporal element (basically the state machine).
> http://www.edn.com/design/integrated-circuit-design/4363949/Decode-a-quadrature-encoder-in-software
> is a good description of the algorithm...

We don't need to describe the state machine because is done inside
the hardware block. We need to tell to the hardware on which edge(s)
it had to count and the number of lines, with that it will give you the
counter value and the direction.

>
> Now if we had some concept of composite signal lines, so a quadrature
> pair was treated as one entity then that might work - but we still aren't
> dealing with edges as such, but rather the state machine change
> they represent.
>
> Agreed the counter interface shouldn't be particularly tied to
> quadrature type signals, but those are one of the most common
> types so we do need to make sure we handle them cleanly.
>

I do believe that describe active edges per lines is a good and generic
way to configure counters and quadrature encoders

> All the other common counter types are more straight forward.
>> >
>> > 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 <[email protected]>
>> >>> + *
>> >>> + * 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 <[email protected]>");
>> >>> +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

2017-07-01 12:25:27

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v2 8/8] iio: counter: Add support for STM32 LPTimer

On Fri, 30 Jun 2017 22:36:48 +0200
Benjamin Gaignard <[email protected]> wrote:

> 2017-06-30 20:19 GMT+02:00 Jonathan Cameron <[email protected]>:
> > On Tue, 27 Jun 2017 10:21:43 +0200
> > Benjamin Gaignard <[email protected]> wrote:
> >
> >> 2017-06-26 22:29 GMT+02:00 William Breathitt Gray <[email protected]>:
> >> > On Sat, Jun 24, 2017 at 09:35:39PM +0100, Jonathan Cameron wrote:
> >> >>On Wed, 21 Jun 2017 16:30:15 +0200
> >> >>Fabrice Gasnier <[email protected]> wrote:
> >> >>
> >> >>> Add support for STM32 Low-Power Timer, that can be used as counter
> >> >>> or quadrature encoder.
> >> >>>
> >> >>> Signed-off-by: Fabrice Gasnier <[email protected]>
> >> >>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: [email protected]
> >> >>> +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: [email protected]
> >> >>> +Description:
> >> >>> + Reading returns the list possible quadrature modes.
> >> >>> +
> >> >>> +What: /sys/bus/iio/devices/iio:deviceX/in_count0_quadrature_mode
> >> >>> +KernelVersion: 4.13
> >> >>> +Contact: [email protected]
> >> >>> +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: [email protected]
> >> >>> +Description:
> >> >>> + Reading returns the list possible active edges.
> >> >>> +
> >> >>> +What: /sys/bus/iio/devices/iio:deviceX/in_count0_polarity
> >> >>> +KernelVersion: 4.13
> >> >>> +Contact: [email protected]
> >> >>> +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.
> > As I read this, the complexities of quadrature counting aren't covered...
> > The reason it works and can tell direction is down to order of state
> > transitions across the two channels. So how do we describe that? It's not
> > even the case that a particular existing state is relevant, there is
> > a temporal element (basically the state machine).
> > http://www.edn.com/design/integrated-circuit-design/4363949/Decode-a-quadrature-encoder-in-software
> > is a good description of the algorithm...
>
> We don't need to describe the state machine because is done inside
> the hardware block. We need to tell to the hardware on which edge(s)
> it had to count and the number of lines, with that it will give you the
> counter value and the direction.
Agreed, we don't need the state machine, but...

We need it to be apparent to userspace that this is a quadrature
channel. We don't need to control that, but we need userspace to be aware
in some fashion. Only then does the question of counting up and down on
particular edges make sense - because on a quadrature channel it will only
do this under some circumstance. Every rising A edge does not result in
a count up for example - if we are going the other way, it would be a count
down only on the falling edge.
>
> >
> > Now if we had some concept of composite signal lines, so a quadrature
> > pair was treated as one entity then that might work - but we still aren't
> > dealing with edges as such, but rather the state machine change
> > they represent.
> >
> > Agreed the counter interface shouldn't be particularly tied to
> > quadrature type signals, but those are one of the most common
> > types so we do need to make sure we handle them cleanly.
> >
>
> I do believe that describe active edges per lines is a good and generic
> way to configure counters and quadrature encoders
Configure perhaps but I'm not following how userspace or a user is supposed
to know whether a channel is part of a quadrature pair rather than a
single line where say a rising edge always means count up.

Counting on particular edges of particular lines can't give this information
(as far as can see anyway - feel free to prove me wrong with examples!)

Jonathan
>
> > All the other common counter types are more straight forward.
> >> >
> >> > 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 <[email protected]>
> >> >>> + *
> >> >>> + * 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 <[email protected]>");
> >> >>> +MODULE_ALIAS("platform:stm32-lptimer-counter");
> >> >>> +MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
> >> >>> +MODULE_LICENSE("GPL v2");
> >> >>
> >>
> >>
> >>
> >
>
>
>

2017-07-01 13:40:51

by William Breathitt Gray

[permalink] [raw]
Subject: Re: [PATCH v2 8/8] iio: counter: Add support for STM32 LPTimer

On Sat, Jul 01, 2017 at 01:25:18PM +0100, Jonathan Cameron wrote:
>On Fri, 30 Jun 2017 22:36:48 +0200
>Benjamin Gaignard <[email protected]> wrote:
>
>> 2017-06-30 20:19 GMT+02:00 Jonathan Cameron <[email protected]>:
>> > On Tue, 27 Jun 2017 10:21:43 +0200
>> > Benjamin Gaignard <[email protected]> wrote:
>> >
>> >> 2017-06-26 22:29 GMT+02:00 William Breathitt Gray <[email protected]>:
>> >> > On Sat, Jun 24, 2017 at 09:35:39PM +0100, Jonathan Cameron wrote:
>> >> >>On Wed, 21 Jun 2017 16:30:15 +0200
>> >> >>Fabrice Gasnier <[email protected]> wrote:
>> >> >>
>> >> >>> Add support for STM32 Low-Power Timer, that can be used as counter
>> >> >>> or quadrature encoder.
>> >> >>>
>> >> >>> Signed-off-by: Fabrice Gasnier <[email protected]>
>> >> >>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: [email protected]
>> >> >>> +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: [email protected]
>> >> >>> +Description:
>> >> >>> + Reading returns the list possible quadrature modes.
>> >> >>> +
>> >> >>> +What: /sys/bus/iio/devices/iio:deviceX/in_count0_quadrature_mode
>> >> >>> +KernelVersion: 4.13
>> >> >>> +Contact: [email protected]
>> >> >>> +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: [email protected]
>> >> >>> +Description:
>> >> >>> + Reading returns the list possible active edges.
>> >> >>> +
>> >> >>> +What: /sys/bus/iio/devices/iio:deviceX/in_count0_polarity
>> >> >>> +KernelVersion: 4.13
>> >> >>> +Contact: [email protected]
>> >> >>> +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.
>> > As I read this, the complexities of quadrature counting aren't covered...
>> > The reason it works and can tell direction is down to order of state
>> > transitions across the two channels. So how do we describe that? It's not
>> > even the case that a particular existing state is relevant, there is
>> > a temporal element (basically the state machine).
>> > http://www.edn.com/design/integrated-circuit-design/4363949/Decode-a-quadrature-encoder-in-software
>> > is a good description of the algorithm...
>>
>> We don't need to describe the state machine because is done inside
>> the hardware block. We need to tell to the hardware on which edge(s)
>> it had to count and the number of lines, with that it will give you the
>> counter value and the direction.
>Agreed, we don't need the state machine, but...
>
>We need it to be apparent to userspace that this is a quadrature
>channel. We don't need to control that, but we need userspace to be aware
>in some fashion. Only then does the question of counting up and down on
>particular edges make sense - because on a quadrature channel it will only
>do this under some circumstance. Every rising A edge does not result in
>a count up for example - if we are going the other way, it would be a count
>down only on the falling edge.
>>
>> >
>> > Now if we had some concept of composite signal lines, so a quadrature
>> > pair was treated as one entity then that might work - but we still aren't
>> > dealing with edges as such, but rather the state machine change
>> > they represent.
>> >
>> > Agreed the counter interface shouldn't be particularly tied to
>> > quadrature type signals, but those are one of the most common
>> > types so we do need to make sure we handle them cleanly.
>> >
>>
>> I do believe that describe active edges per lines is a good and generic
>> way to configure counters and quadrature encoders
>Configure perhaps but I'm not following how userspace or a user is supposed
>to know whether a channel is part of a quadrature pair rather than a
>single line where say a rising edge always means count up.
>
>Counting on particular edges of particular lines can't give this information
>(as far as can see anyway - feel free to prove me wrong with examples!)
>
>Jonathan

You're right: if a single quadrature channel signal flatlines, the count
ceases to increase/decrease, regardless of whether the other channels
keep pulsing; that's a very important quality of quadrature signals.

However, I believe the interface I described can communicate the
quadrature nature of the signal lines via the accumulation operation
attribute.

For example, if the edges of the signal lines should be independently
evaluated (i.e. non-quadrature mode), then the accumulation operation
attribute may be set to simply "increase count." The signal lines
themselves can then be configured to the desired line state trigger
(rising edge, falling edge, etc.), and each signal line triggers the
"increase count" accumulation operation independently given their
respective trigger condition.

Now, your concern was the case of quadrature signals: i.e. how do we
communicate the dependence of signal line state as it relates to the
count operation. In this scenario, I would imagine the accumulation
operation attribute to allow a respective "quadrature" mode value. So
then, if quadrature x1 is desired, then the accumulation operation
attribute is set to "quadrature x1" or such.

The desired edge trigger configuration can then be set on the desired
signal line. Note that in this case the driver would have to perform
sanity checks to ensure a valid configuration is selected for the
desired accumulation operation; e.g. "quadrature x1" should only have a
single signal line triggering the accumulation operation -- attempts to
configure multiple signal lines for edge triggering should yield back a
respective "invalid operation" error code to userspace, until either the
accumulation operation is changed or a different valid configuration is
set.

William Breathitt Gray

>>
>> > All the other common counter types are more straight forward.
>> >> >
>> >> > 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 <[email protected]>
>> >> >>> + *
>> >> >>> + * 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 <[email protected]>");
>> >> >>> +MODULE_ALIAS("platform:stm32-lptimer-counter");
>> >> >>> +MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
>> >> >>> +MODULE_LICENSE("GPL v2");
>> >> >>
>> >>
>> >>
>> >>
>> >
>>
>>
>>
>

2017-07-01 18:09:25

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v2 8/8] iio: counter: Add support for STM32 LPTimer

On Sat, 1 Jul 2017 09:40:35 -0400
William Breathitt Gray <[email protected]> wrote:

> On Sat, Jul 01, 2017 at 01:25:18PM +0100, Jonathan Cameron wrote:
> >On Fri, 30 Jun 2017 22:36:48 +0200
> >Benjamin Gaignard <[email protected]> wrote:
> >
> >> 2017-06-30 20:19 GMT+02:00 Jonathan Cameron <[email protected]>:
> >> > On Tue, 27 Jun 2017 10:21:43 +0200
> >> > Benjamin Gaignard <[email protected]> wrote:
> >> >
> >> >> 2017-06-26 22:29 GMT+02:00 William Breathitt Gray <[email protected]>:
> >> >> > On Sat, Jun 24, 2017 at 09:35:39PM +0100, Jonathan Cameron wrote:
> >> >> >>On Wed, 21 Jun 2017 16:30:15 +0200
> >> >> >>Fabrice Gasnier <[email protected]> wrote:
> >> >> >>
> >> >> >>> Add support for STM32 Low-Power Timer, that can be used as counter
> >> >> >>> or quadrature encoder.
> >> >> >>>
> >> >> >>> Signed-off-by: Fabrice Gasnier <[email protected]>
> >> >> >>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: [email protected]
> >> >> >>> +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: [email protected]
> >> >> >>> +Description:
> >> >> >>> + Reading returns the list possible quadrature modes.
> >> >> >>> +
> >> >> >>> +What: /sys/bus/iio/devices/iio:deviceX/in_count0_quadrature_mode
> >> >> >>> +KernelVersion: 4.13
> >> >> >>> +Contact: [email protected]
> >> >> >>> +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: [email protected]
> >> >> >>> +Description:
> >> >> >>> + Reading returns the list possible active edges.
> >> >> >>> +
> >> >> >>> +What: /sys/bus/iio/devices/iio:deviceX/in_count0_polarity
> >> >> >>> +KernelVersion: 4.13
> >> >> >>> +Contact: [email protected]
> >> >> >>> +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.
> >> > As I read this, the complexities of quadrature counting aren't covered...
> >> > The reason it works and can tell direction is down to order of state
> >> > transitions across the two channels. So how do we describe that? It's not
> >> > even the case that a particular existing state is relevant, there is
> >> > a temporal element (basically the state machine).
> >> > http://www.edn.com/design/integrated-circuit-design/4363949/Decode-a-quadrature-encoder-in-software
> >> > is a good description of the algorithm...
> >>
> >> We don't need to describe the state machine because is done inside
> >> the hardware block. We need to tell to the hardware on which edge(s)
> >> it had to count and the number of lines, with that it will give you the
> >> counter value and the direction.
> >Agreed, we don't need the state machine, but...
> >
> >We need it to be apparent to userspace that this is a quadrature
> >channel. We don't need to control that, but we need userspace to be aware
> >in some fashion. Only then does the question of counting up and down on
> >particular edges make sense - because on a quadrature channel it will only
> >do this under some circumstance. Every rising A edge does not result in
> >a count up for example - if we are going the other way, it would be a count
> >down only on the falling edge.
> >>
> >> >
> >> > Now if we had some concept of composite signal lines, so a quadrature
> >> > pair was treated as one entity then that might work - but we still aren't
> >> > dealing with edges as such, but rather the state machine change
> >> > they represent.
> >> >
> >> > Agreed the counter interface shouldn't be particularly tied to
> >> > quadrature type signals, but those are one of the most common
> >> > types so we do need to make sure we handle them cleanly.
> >> >
> >>
> >> I do believe that describe active edges per lines is a good and generic
> >> way to configure counters and quadrature encoders
> >Configure perhaps but I'm not following how userspace or a user is supposed
> >to know whether a channel is part of a quadrature pair rather than a
> >single line where say a rising edge always means count up.
> >
> >Counting on particular edges of particular lines can't give this information
> >(as far as can see anyway - feel free to prove me wrong with examples!)
> >
> >Jonathan
>
> You're right: if a single quadrature channel signal flatlines, the count
> ceases to increase/decrease, regardless of whether the other channels
> keep pulsing; that's a very important quality of quadrature signals.
>
> However, I believe the interface I described can communicate the
> quadrature nature of the signal lines via the accumulation operation
> attribute.
>
> For example, if the edges of the signal lines should be independently
> evaluated (i.e. non-quadrature mode), then the accumulation operation
> attribute may be set to simply "increase count." The signal lines
> themselves can then be configured to the desired line state trigger
> (rising edge, falling edge, etc.), and each signal line triggers the
> "increase count" accumulation operation independently given their
> respective trigger condition.
>
> Now, your concern was the case of quadrature signals: i.e. how do we
> communicate the dependence of signal line state as it relates to the
> count operation. In this scenario, I would imagine the accumulation
> operation attribute to allow a respective "quadrature" mode value. So
> then, if quadrature x1 is desired, then the accumulation operation
> attribute is set to "quadrature x1" or such.
>
> The desired edge trigger configuration can then be set on the desired
> signal line. Note that in this case the driver would have to perform
> sanity checks to ensure a valid configuration is selected for the
> desired accumulation operation; e.g. "quadrature x1" should only have a
> single signal line triggering the accumulation operation -- attempts to
> configure multiple signal lines for edge triggering should yield back a
> respective "invalid operation" error code to userspace, until either the
> accumulation operation is changed or a different valid configuration is
> set.
Hmm. That might work reasonably well, but seems it might have some
redundancy in configuration. Can handle that by having one attribute
change effect multiple options.

For example, 'quadrature x4' would set both A and B lines to be on
both edges.

Do we need to make quadrature pairs explicit in some way?

I hope there are no devices out there where multiple quadrature pairs
can effect a single counter. Can't immediately think of what you
could use such a beast for, but that doesn't mean hardware doesn't
implement it!

Jonathan
>
> William Breathitt Gray
>
> >>
> >> > All the other common counter types are more straight forward.
> >> >> >
> >> >> > 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 <[email protected]>
> >> >> >>> + *
> >> >> >>> + * 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 <[email protected]>");
> >> >> >>> +MODULE_ALIAS("platform:stm32-lptimer-counter");
> >> >> >>> +MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
> >> >> >>> +MODULE_LICENSE("GPL v2");
> >> >> >>
> >> >>
> >> >>
> >> >>
> >> >
> >>
> >>
> >>
> >
>

2017-07-03 01:03:12

by William Breathitt Gray

[permalink] [raw]
Subject: Re: [PATCH v2 8/8] iio: counter: Add support for STM32 LPTimer

On Sat, Jul 01, 2017 at 07:09:17PM +0100, Jonathan Cameron wrote:
>On Sat, 1 Jul 2017 09:40:35 -0400
>William Breathitt Gray <[email protected]> wrote:
>
>> On Sat, Jul 01, 2017 at 01:25:18PM +0100, Jonathan Cameron wrote:
>> >On Fri, 30 Jun 2017 22:36:48 +0200
>> >Benjamin Gaignard <[email protected]> wrote:
>> >
>> >> 2017-06-30 20:19 GMT+02:00 Jonathan Cameron <[email protected]>:
>> >> > On Tue, 27 Jun 2017 10:21:43 +0200
>> >> > Benjamin Gaignard <[email protected]> wrote:
>> >> >
>> >> >> 2017-06-26 22:29 GMT+02:00 William Breathitt Gray <[email protected]>:
>> >> >> > On Sat, Jun 24, 2017 at 09:35:39PM +0100, Jonathan Cameron wrote:
>> >> >> >>On Wed, 21 Jun 2017 16:30:15 +0200
>> >> >> >>Fabrice Gasnier <[email protected]> wrote:
>> >> >> >>
>> >> >> >>> Add support for STM32 Low-Power Timer, that can be used as counter
>> >> >> >>> or quadrature encoder.
>> >> >> >>>
>> >> >> >>> Signed-off-by: Fabrice Gasnier <[email protected]>
>> >> >> >>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: [email protected]
>> >> >> >>> +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: [email protected]
>> >> >> >>> +Description:
>> >> >> >>> + Reading returns the list possible quadrature modes.
>> >> >> >>> +
>> >> >> >>> +What: /sys/bus/iio/devices/iio:deviceX/in_count0_quadrature_mode
>> >> >> >>> +KernelVersion: 4.13
>> >> >> >>> +Contact: [email protected]
>> >> >> >>> +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: [email protected]
>> >> >> >>> +Description:
>> >> >> >>> + Reading returns the list possible active edges.
>> >> >> >>> +
>> >> >> >>> +What: /sys/bus/iio/devices/iio:deviceX/in_count0_polarity
>> >> >> >>> +KernelVersion: 4.13
>> >> >> >>> +Contact: [email protected]
>> >> >> >>> +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.
>> >> > As I read this, the complexities of quadrature counting aren't covered...
>> >> > The reason it works and can tell direction is down to order of state
>> >> > transitions across the two channels. So how do we describe that? It's not
>> >> > even the case that a particular existing state is relevant, there is
>> >> > a temporal element (basically the state machine).
>> >> > http://www.edn.com/design/integrated-circuit-design/4363949/Decode-a-quadrature-encoder-in-software
>> >> > is a good description of the algorithm...
>> >>
>> >> We don't need to describe the state machine because is done inside
>> >> the hardware block. We need to tell to the hardware on which edge(s)
>> >> it had to count and the number of lines, with that it will give you the
>> >> counter value and the direction.
>> >Agreed, we don't need the state machine, but...
>> >
>> >We need it to be apparent to userspace that this is a quadrature
>> >channel. We don't need to control that, but we need userspace to be aware
>> >in some fashion. Only then does the question of counting up and down on
>> >particular edges make sense - because on a quadrature channel it will only
>> >do this under some circumstance. Every rising A edge does not result in
>> >a count up for example - if we are going the other way, it would be a count
>> >down only on the falling edge.
>> >>
>> >> >
>> >> > Now if we had some concept of composite signal lines, so a quadrature
>> >> > pair was treated as one entity then that might work - but we still aren't
>> >> > dealing with edges as such, but rather the state machine change
>> >> > they represent.
>> >> >
>> >> > Agreed the counter interface shouldn't be particularly tied to
>> >> > quadrature type signals, but those are one of the most common
>> >> > types so we do need to make sure we handle them cleanly.
>> >> >
>> >>
>> >> I do believe that describe active edges per lines is a good and generic
>> >> way to configure counters and quadrature encoders
>> >Configure perhaps but I'm not following how userspace or a user is supposed
>> >to know whether a channel is part of a quadrature pair rather than a
>> >single line where say a rising edge always means count up.
>> >
>> >Counting on particular edges of particular lines can't give this information
>> >(as far as can see anyway - feel free to prove me wrong with examples!)
>> >
>> >Jonathan
>>
>> You're right: if a single quadrature channel signal flatlines, the count
>> ceases to increase/decrease, regardless of whether the other channels
>> keep pulsing; that's a very important quality of quadrature signals.
>>
>> However, I believe the interface I described can communicate the
>> quadrature nature of the signal lines via the accumulation operation
>> attribute.
>>
>> For example, if the edges of the signal lines should be independently
>> evaluated (i.e. non-quadrature mode), then the accumulation operation
>> attribute may be set to simply "increase count." The signal lines
>> themselves can then be configured to the desired line state trigger
>> (rising edge, falling edge, etc.), and each signal line triggers the
>> "increase count" accumulation operation independently given their
>> respective trigger condition.
>>
>> Now, your concern was the case of quadrature signals: i.e. how do we
>> communicate the dependence of signal line state as it relates to the
>> count operation. In this scenario, I would imagine the accumulation
>> operation attribute to allow a respective "quadrature" mode value. So
>> then, if quadrature x1 is desired, then the accumulation operation
>> attribute is set to "quadrature x1" or such.
>>
>> The desired edge trigger configuration can then be set on the desired
>> signal line. Note that in this case the driver would have to perform
>> sanity checks to ensure a valid configuration is selected for the
>> desired accumulation operation; e.g. "quadrature x1" should only have a
>> single signal line triggering the accumulation operation -- attempts to
>> configure multiple signal lines for edge triggering should yield back a
>> respective "invalid operation" error code to userspace, until either the
>> accumulation operation is changed or a different valid configuration is
>> set.
>Hmm. That might work reasonably well, but seems it might have some
>redundancy in configuration. Can handle that by having one attribute
>change effect multiple options.
>
>For example, 'quadrature x4' would set both A and B lines to be on
>both edges.
>
>Do we need to make quadrature pairs explicit in some way?
>
>I hope there are no devices out there where multiple quadrature pairs
>can effect a single counter. Can't immediately think of what you
>could use such a beast for, but that doesn't mean hardware doesn't
>implement it!
>
>Jonathan

Yes, this interface is effectively the generic counter template --
essentially any device that qualifies as a counter should be capable of
exposure via this generic counter interface. However, as you noted there
are subtypes of counter devices ubiquitous enough to warrant further
standardization, such as quadrature encoder counters.

I'm not certain yet what the best way to expose common quadrature
configurations would be, but for now I can see some sort of
"quadrature_mode" attribute being useful (it seems this configuration
option reoccurs often among the quadrature devices). Then, as you
described, we can have common presets available (x1, x2, x4, etc.) that
would automatically set the correlating configuration on the respective
signal line and accumulation operation attributes. This way, both user
and driver author only need to setup and interact via a common
quadrature counter interface rather than the more tedious generic
counter interface (while still providing a compliant and working generic
counter interface as a baseline of functionality).

Specifying quadrature pairs explicitly may not be necessary if the
respective signal line attributes are automatically configured; but on
the other hand I can see some benefit to simplifying the communication
of such information if we already know we are dealing with a quadrature
device. I'm unfortunately undecided as of right now, but perhaps another
person may have some ideas of what such an interface would look like.
It's very likely that the majority of counter drivers will be for
quadrature devices, so it makes sense that this area of the interface
will grow to accomondate the redundancy of code we see, and we should
embrace that if it makes interaction with these devices simplier in the
end.

Your description of a theoretical device where multiple quadrature pairs
effect a single counter is interesting. This is where the utility of the
generic counter interface as a fallback for unique devices may be
exemplified: such an effect of configured signal lines can be
communicated via a custom accumulation operation mode, perhaps specified
as "dual quadrature x1" or such to indicate the additive combined nature
of the multiple quadrature pairs. Or perhaps, if we do add an explicit
quadrature pair attribute, we can have an association attribute to tie a
quadrature pair (or perhaps more) with an associated quadrature mode and
accumulation value.

While I'm not certain yet of what an explicit quadrature counter
interface would look like, I believe the generic counter interface is
robust enough to provide at least some utility as a baseline interface
for counter devices in general. I'll try to submit a patch adding such a
generic counter interface some time this week. From that point, we
should be capable of introducing a more specific quadrature counter
interface, which is in no doubt necessary to reduce the redundant code
that has been popping up among these drivers.

William Breathitt Gray

>>
>> William Breathitt Gray
>>
>> >>
>> >> > All the other common counter types are more straight forward.
>> >> >> >
>> >> >> > 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 <[email protected]>
>> >> >> >>> + *
>> >> >> >>> + * 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 <[email protected]>");
>> >> >> >>> +MODULE_ALIAS("platform:stm32-lptimer-counter");
>> >> >> >>> +MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
>> >> >> >>> +MODULE_LICENSE("GPL v2");
>> >> >> >>
>> >> >>
>> >> >>
>> >> >>
>> >> >
>> >>
>> >>
>> >>
>> >
>>
>

2017-07-06 07:43:27

by Thierry Reding

[permalink] [raw]
Subject: Re: [PATCH v2 4/8] pwm: Add STM32 LPTimer PWM driver

On Wed, Jun 21, 2017 at 04:30:11PM +0200, Fabrice Gasnier wrote:
> Add support for single PWM channel on Low-Power Timer, that can be
> found on some STM32 platforms.
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
> ---
> Changes in v2:
> - s/Low Power/Low-Power
> - update few comment lines
> ---
> drivers/pwm/Kconfig | 10 +++
> drivers/pwm/Makefile | 1 +
> drivers/pwm/pwm-stm32-lp.c | 216 +++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 227 insertions(+)
> create mode 100644 drivers/pwm/pwm-stm32-lp.c
>
> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> index 313c107..7cb982b 100644
> --- a/drivers/pwm/Kconfig
> +++ b/drivers/pwm/Kconfig
> @@ -417,6 +417,16 @@ config PWM_STM32
> To compile this driver as a module, choose M here: the module
> will be called pwm-stm32.
>
> +config PWM_STM32_LP
> + tristate "STMicroelectronics STM32 PWM LP"
> + depends on MFD_STM32_LPTIMER || COMPILE_TEST
> + help
> + Generic PWM framework driver for STMicroelectronics STM32 SoCs
> + with Low-Power Timer (LPTIM).
> +
> + To compile this driver as a module, choose M here: the module
> + will be called pwm-stm32-lp.
> +
> config PWM_STMPE
> bool "STMPE expander PWM export"
> depends on MFD_STMPE
> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
> index 93da1f7..a3a4bee 100644
> --- a/drivers/pwm/Makefile
> +++ b/drivers/pwm/Makefile
> @@ -40,6 +40,7 @@ obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
> obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o
> obj-$(CONFIG_PWM_STI) += pwm-sti.o
> obj-$(CONFIG_PWM_STM32) += pwm-stm32.o
> +obj-$(CONFIG_PWM_STM32_LP) += pwm-stm32-lp.o
> obj-$(CONFIG_PWM_STMPE) += pwm-stmpe.o
> obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o
> obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o
> diff --git a/drivers/pwm/pwm-stm32-lp.c b/drivers/pwm/pwm-stm32-lp.c
> new file mode 100644
> index 0000000..eb997a8
> --- /dev/null
> +++ b/drivers/pwm/pwm-stm32-lp.c
> @@ -0,0 +1,216 @@
> +/*
> + * STM32 Low-Power Timer PWM driver
> + *
> + * Copyright (C) STMicroelectronics 2017
> + *
> + * Author: Gerald Baeza <[email protected]>
> + *
> + * License terms: GNU General Public License (GPL), version 2
> + *
> + * Inspired by Gerald Baeza's pwm-stm32 driver
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/mfd/stm32-lptimer.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/pwm.h>
> +
> +struct stm32_pwm_lp {
> + struct pwm_chip chip;
> + struct clk *clk;
> + struct regmap *regmap;
> +};
> +
> +static inline struct stm32_pwm_lp *to_stm32_pwm_lp(struct pwm_chip *chip)
> +{
> + return container_of(chip, struct stm32_pwm_lp, chip);
> +}
> +
> +static const u8 prescalers[] = {1, 2, 4, 8, 16, 32, 64, 128};
> +
> +static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm,
> + struct pwm_state *state)
> +{
> + struct stm32_pwm_lp *priv = to_stm32_pwm_lp(chip);
> + unsigned long long prd, div, dty;
> + struct pwm_state cstate;
> + u32 val, mask, cfgr, wavpol, presc = 0;
> + bool reenable = false;
> + int ret;
> +
> + pwm_get_state(pwm, &cstate);
> +
> + if (!state->enabled) {
> + if (cstate.enabled) {
> + /* Disable LP timer */
> + ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
> + if (ret)
> + return ret;
> + clk_disable(priv->clk);
> + }
> + return 0;
> + }
> +
> + /* Calculate the period and prescaler value */
> + div = (unsigned long long)clk_get_rate(priv->clk) * state->period;
> + do_div(div, NSEC_PER_SEC);
> + prd = div;
> + while (div > STM32_LPTIM_MAX_ARR) {
> + presc++;
> + if (presc >= ARRAY_SIZE(prescalers)) {
> + dev_err(priv->chip.dev, "max prescaler exceeded\n");
> + return -EINVAL;
> + }
> + div = prd;
> + do_div(div, prescalers[presc]);
> + }
> + prd = div;
> +
> + /* Calculate the duty cycle */
> + dty = prd * state->duty_cycle;
> + do_div(dty, state->period);
> +
> + wavpol = FIELD_PREP(STM32_LPTIM_WAVPOL, state->polarity);
> +
> + if (!cstate.enabled) {
> + ret = clk_enable(priv->clk);
> + if (ret)
> + return ret;
> + }

Why do you need the checks here? Clock enabled are reference counted, so
you could do the clk_enable() unconditionally.

Speaking of which, I don't see a clk_prepare() anywhere. Doesn't the clk
core warn about clk_enable() being called on a clock that's not been
prepared?

> +
> + ret = regmap_read(priv->regmap, STM32_LPTIM_CFGR, &cfgr);
> + if (ret)
> + goto err;
> +
> + if ((wavpol != FIELD_GET(STM32_LPTIM_WAVPOL, cfgr)) ||

This looks wrong to me. Looking at the macro definitions, FIELD_PREP()
will store the shifted value in wavpol, but FIELD_GET() will shift the
value before returning, so you will compare an in-register value with
a field value. I don't see how those could ever match (unless they're
0 or the field is at position 0, which isn't the case for WAVPOL).

> + (presc != FIELD_GET(STM32_LPTIM_PRESC, cfgr))) {
> + val = FIELD_PREP(STM32_LPTIM_PRESC, presc) | wavpol;
> + mask = STM32_LPTIM_PRESC | STM32_LPTIM_WAVPOL;
> +
> + /* Must disable LP timer to modify CFGR */
> + ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
> + if (ret)
> + goto err;
> + reenable = true;

The placement of this is somewhat odd. It suggests that it is somehow
related to the disabling of the LP timer, whereas it really isn't.

> + ret = regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask,
> + val);
> + if (ret)
> + goto err;
> + }
> +
> + if (!cstate.enabled || reenable) {

You have this condition in a couple of places and it's rather difficult
to parse. Maybe this could be simplified a little:

bool reenable = !cstate.enabled;
...
if (...) {
...
reenable = true;
...
}
...
if (reenable) {
...
}

> + /* Must enable LP timer to modify CMP & ARR */
> + ret = regmap_write(priv->regmap, STM32_LPTIM_CR,
> + STM32_LPTIM_ENABLE);
> + if (ret)
> + goto err;
> + }
> +
> + ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, prd - 1);
> + if (ret)
> + goto err;
> +
> + ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, prd - (1 + dty));
> + if (ret)
> + goto err;
> +
> + /* 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) {
> + dev_err(priv->chip.dev, "ARR/CMP registers write issue\n");
> + goto err;
> + }
> + ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,
> + STM32_LPTIM_CMPOKCF_ARROKCF);
> + if (ret)
> + goto err;
> +
> + if (!cstate.enabled || reenable) {
> + /* Start LP timer in continuous mode */
> + ret = regmap_update_bits(priv->regmap, STM32_LPTIM_CR,
> + STM32_LPTIM_CNTSTRT,
> + STM32_LPTIM_CNTSTRT);
> + if (ret) {
> + regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
> + goto err;
> + }
> + }
> +
> + return 0;
> +err:
> + if (!cstate.enabled)
> + clk_disable(priv->clk);

I think you can drop the clk_disable() here as well.

> +
> + return ret;
> +}
> +
> +static const struct pwm_ops stm32_pwm_lp_ops = {
> + .owner = THIS_MODULE,
> + .apply = stm32_pwm_lp_apply,
> +};

You should implement the .get_state() callback as well, otherwise the
atomic PWM support will be somewhat handicapped.

> +
> +static int stm32_pwm_lp_probe(struct platform_device *pdev)
> +{
> + struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
> + struct stm32_pwm_lp *priv;
> + int ret;
> +
> + if (IS_ERR_OR_NULL(ddata))
> + return -EINVAL;

It seems to me like this can never happen. How would you trigger this
condition?

> +
> + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + priv->regmap = ddata->regmap;
> + priv->clk = ddata->clk;
> + if (!priv->regmap || !priv->clk)
> + return -EINVAL;

Likewise for these. the stm32-lptimer driver already checks that these
are valid, which do you need to do it again?

Well, technically you check for !NULL here, whereas stm32-lptimer does
check for IS_ERR(), but neither regmap nor clk looks as though they're
optional, and you won't ever get here if they can't be requested by
stm32-lptimer in the first place.

> +
> + priv->chip.base = -1;
> + priv->chip.dev = &pdev->dev;
> + priv->chip.ops = &stm32_pwm_lp_ops;
> + priv->chip.npwm = 1;
> +
> + ret = pwmchip_add(&priv->chip);
> + if (ret < 0)
> + return ret;
> +
> + platform_set_drvdata(pdev, priv);
> +
> + return 0;
> +}
> +
> +static int stm32_pwm_lp_remove(struct platform_device *pdev)
> +{
> + struct stm32_pwm_lp *priv = platform_get_drvdata(pdev);
> +
> + if (pwm_is_enabled(priv->chip.pwms))
> + pwm_disable(priv->chip.pwms);

It'd be better to use the more idiomatic variant for this:

for (i = 0; i < priv->chip.npwm; i++)
if (pwm_is_enabled(priv->chip.npwm))
pwm_disable(&priv->chip.pwms[i]);

That makes it easier to discern the common pattern and extract a helper,
or move this to the core.

Thierry


Attachments:
(No filename) (8.73 kB)
signature.asc (833.00 B)
Download all attachments

2017-07-07 08:11:17

by Fabrice Gasnier

[permalink] [raw]
Subject: Re: [PATCH v2 4/8] pwm: Add STM32 LPTimer PWM driver

On 07/06/2017 09:43 AM, Thierry Reding wrote:
> On Wed, Jun 21, 2017 at 04:30:11PM +0200, Fabrice Gasnier wrote:
>> Add support for single PWM channel on Low-Power Timer, that can be
>> found on some STM32 platforms.
>>
>> Signed-off-by: Fabrice Gasnier <[email protected]>
>> ---
>> Changes in v2:
>> - s/Low Power/Low-Power
>> - update few comment lines
>> ---
>> drivers/pwm/Kconfig | 10 +++
>> drivers/pwm/Makefile | 1 +
>> drivers/pwm/pwm-stm32-lp.c | 216 +++++++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 227 insertions(+)
>> create mode 100644 drivers/pwm/pwm-stm32-lp.c
>>
>> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
>> index 313c107..7cb982b 100644
>> --- a/drivers/pwm/Kconfig
>> +++ b/drivers/pwm/Kconfig
>> @@ -417,6 +417,16 @@ config PWM_STM32
>> To compile this driver as a module, choose M here: the module
>> will be called pwm-stm32.
>>
>> +config PWM_STM32_LP
>> + tristate "STMicroelectronics STM32 PWM LP"
>> + depends on MFD_STM32_LPTIMER || COMPILE_TEST
>> + help
>> + Generic PWM framework driver for STMicroelectronics STM32 SoCs
>> + with Low-Power Timer (LPTIM).
>> +
>> + To compile this driver as a module, choose M here: the module
>> + will be called pwm-stm32-lp.
>> +
>> config PWM_STMPE
>> bool "STMPE expander PWM export"
>> depends on MFD_STMPE
>> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
>> index 93da1f7..a3a4bee 100644
>> --- a/drivers/pwm/Makefile
>> +++ b/drivers/pwm/Makefile
>> @@ -40,6 +40,7 @@ obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
>> obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o
>> obj-$(CONFIG_PWM_STI) += pwm-sti.o
>> obj-$(CONFIG_PWM_STM32) += pwm-stm32.o
>> +obj-$(CONFIG_PWM_STM32_LP) += pwm-stm32-lp.o
>> obj-$(CONFIG_PWM_STMPE) += pwm-stmpe.o
>> obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o
>> obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o
>> diff --git a/drivers/pwm/pwm-stm32-lp.c b/drivers/pwm/pwm-stm32-lp.c
>> new file mode 100644
>> index 0000000..eb997a8
>> --- /dev/null
>> +++ b/drivers/pwm/pwm-stm32-lp.c
>> @@ -0,0 +1,216 @@
>> +/*
>> + * STM32 Low-Power Timer PWM driver
>> + *
>> + * Copyright (C) STMicroelectronics 2017
>> + *
>> + * Author: Gerald Baeza <[email protected]>
>> + *
>> + * License terms: GNU General Public License (GPL), version 2
>> + *
>> + * Inspired by Gerald Baeza's pwm-stm32 driver
>> + */
>> +
>> +#include <linux/bitfield.h>
>> +#include <linux/mfd/stm32-lptimer.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pwm.h>
>> +
>> +struct stm32_pwm_lp {
>> + struct pwm_chip chip;
>> + struct clk *clk;
>> + struct regmap *regmap;
>> +};
>> +
>> +static inline struct stm32_pwm_lp *to_stm32_pwm_lp(struct pwm_chip *chip)
>> +{
>> + return container_of(chip, struct stm32_pwm_lp, chip);
>> +}
>> +
>> +static const u8 prescalers[] = {1, 2, 4, 8, 16, 32, 64, 128};
>> +
>> +static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm,
>> + struct pwm_state *state)
>> +{
>> + struct stm32_pwm_lp *priv = to_stm32_pwm_lp(chip);
>> + unsigned long long prd, div, dty;
>> + struct pwm_state cstate;
>> + u32 val, mask, cfgr, wavpol, presc = 0;
>> + bool reenable = false;
>> + int ret;
>> +
>> + pwm_get_state(pwm, &cstate);
>> +
>> + if (!state->enabled) {
>> + if (cstate.enabled) {
>> + /* Disable LP timer */
>> + ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
>> + if (ret)
>> + return ret;
>> + clk_disable(priv->clk);
>> + }
>> + return 0;
>> + }
>> +
>> + /* Calculate the period and prescaler value */
>> + div = (unsigned long long)clk_get_rate(priv->clk) * state->period;
>> + do_div(div, NSEC_PER_SEC);
>> + prd = div;
>> + while (div > STM32_LPTIM_MAX_ARR) {
>> + presc++;
>> + if (presc >= ARRAY_SIZE(prescalers)) {
>> + dev_err(priv->chip.dev, "max prescaler exceeded\n");
>> + return -EINVAL;
>> + }
>> + div = prd;
>> + do_div(div, prescalers[presc]);
>> + }
>> + prd = div;
>> +
>> + /* Calculate the duty cycle */
>> + dty = prd * state->duty_cycle;
>> + do_div(dty, state->period);
>> +
>> + wavpol = FIELD_PREP(STM32_LPTIM_WAVPOL, state->polarity);
>> +
>> + if (!cstate.enabled) {
>> + ret = clk_enable(priv->clk);
>> + if (ret)
>> + return ret;
>> + }
>
> Why do you need the checks here? Clock enabled are reference counted, so
> you could do the clk_enable() unconditionally.

Hi Thierry,

This clock is used to generate PWM (source for LP timer counter). I
enable it here as:
- required state is 'enabled'
- current state is 'disabled'.
PWM is being turned on: first enable clock, then configure & enable PWM
bellow.

The opposite is done earlier, at the beginning of this routine:
- required state is 'disabled'
- current state is 'enabled'
PWM is turned off, then clock is disabled.

Enable count should be balanced, and clock is enabled when required
(e.g. when PWM is 'on'). Doing it unconditionally here may cause
unbalanced enable count (e.g. any duty_cycle update would increase
enable count)
Is it ok to keep this ?

>
> Speaking of which, I don't see a clk_prepare() anywhere. Doesn't the clk
> core warn about clk_enable() being called on a clock that's not been
> prepared?

clk_get() and clk_prepare() happens in regmap layer, when probing mfd part:
-> stm32_lptimer_probe()
-> devm_regmap_init_mmio_clk()
-> __devm_regmap_init_mmio_clk()
-> regmap_mmio_gen_context()

>
>> +
>> + ret = regmap_read(priv->regmap, STM32_LPTIM_CFGR, &cfgr);
>> + if (ret)
>> + goto err;
>> +
>> + if ((wavpol != FIELD_GET(STM32_LPTIM_WAVPOL, cfgr)) ||
>
> This looks wrong to me. Looking at the macro definitions, FIELD_PREP()
> will store the shifted value in wavpol, but FIELD_GET() will shift the
> value before returning, so you will compare an in-register value with
> a field value. I don't see how those could ever match (unless they're
> 0 or the field is at position 0, which isn't the case for WAVPOL).

Ho, you're right: thanks for pointing this!
I did some test on wavepol, but noticed nothing wrong on PWM signals.
I guess I was lucky! I'll fix it in v3.

>
>> + (presc != FIELD_GET(STM32_LPTIM_PRESC, cfgr))) {
>> + val = FIELD_PREP(STM32_LPTIM_PRESC, presc) | wavpol;
>> + mask = STM32_LPTIM_PRESC | STM32_LPTIM_WAVPOL;
>> +
>> + /* Must disable LP timer to modify CFGR */
>> + ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
>> + if (ret)
>> + goto err;
>> + reenable = true;
>
> The placement of this is somewhat odd. It suggests that it is somehow
> related to the disabling of the LP timer, whereas it really isn't.

In case of prescaler or polarity change, CFGR register needs to be
updated. CFGR register must be modified only when LP timer HW is disabled.
- Initial choice is to use this flag, to temporarily disable HW, update
cfgr, then re-enable it. More thinking about this...

- Another choice could be to refuse such a 'live' change and report
(busy?) error ? Then user would have to explicitly disable it, configure
new setting and re-enable it.

Please let me know your opinion.

>
>> + ret = regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask,
>> + val);
>> + if (ret)
>> + goto err;
>> + }
>> +
>> + if (!cstate.enabled || reenable) {
>
> You have this condition in a couple of places and it's rather difficult
> to parse. Maybe this could be simplified a little:
>
> bool reenable = !cstate.enabled;
> ...
> if (...) {
> ...
> reenable = true;
> ...
> }
> ...
> if (reenable) {
> ...
> }

If I keep current 'reenable' approach (CFGR update), I'll adopt your
proposal to simplify this. Or, maybe this can be dropped (e.g. report
busy error above ?).

>
>> + /* Must enable LP timer to modify CMP & ARR */
>> + ret = regmap_write(priv->regmap, STM32_LPTIM_CR,
>> + STM32_LPTIM_ENABLE);
>> + if (ret)
>> + goto err;
>> + }
>> +
>> + ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, prd - 1);
>> + if (ret)
>> + goto err;
>> +
>> + ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, prd - (1 + dty));
>> + if (ret)
>> + goto err;
>> +
>> + /* 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) {
>> + dev_err(priv->chip.dev, "ARR/CMP registers write issue\n");
>> + goto err;
>> + }
>> + ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,
>> + STM32_LPTIM_CMPOKCF_ARROKCF);
>> + if (ret)
>> + goto err;
>> +
>> + if (!cstate.enabled || reenable) {
>> + /* Start LP timer in continuous mode */
>> + ret = regmap_update_bits(priv->regmap, STM32_LPTIM_CR,
>> + STM32_LPTIM_CNTSTRT,
>> + STM32_LPTIM_CNTSTRT);
>> + if (ret) {
>> + regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
>> + goto err;
>> + }
>> + }
>> +
>> + return 0;
>> +err:
>> + if (!cstate.enabled)
>> + clk_disable(priv->clk);
>
> I think you can drop the clk_disable() here as well.

This is necessary to balance earlier clk_enable() in case of error.

>
>> +
>> + return ret;
>> +}
>> +
>> +static const struct pwm_ops stm32_pwm_lp_ops = {
>> + .owner = THIS_MODULE,
>> + .apply = stm32_pwm_lp_apply,
>> +};
>
> You should implement the .get_state() callback as well, otherwise the
> atomic PWM support will be somewhat handicapped.

Ok, I'll have a look at it.

>
>> +
>> +static int stm32_pwm_lp_probe(struct platform_device *pdev)
>> +{
>> + struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
>> + struct stm32_pwm_lp *priv;
>> + int ret;
>> +
>> + if (IS_ERR_OR_NULL(ddata))
>> + return -EINVAL;
>
> It seems to me like this can never happen. How would you trigger this
> condition?

Bad dt configuration can trigger this error: thinking of a
'st,stm32-pwm-lp' dt node without proper mfd parent. Do you want me to
drop this ?
(or add comment about it ?)

>
>> +
>> + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
>> + if (!priv)
>> + return -ENOMEM;
>> +
>> + priv->regmap = ddata->regmap;
>> + priv->clk = ddata->clk;
>> + if (!priv->regmap || !priv->clk)
>> + return -EINVAL;
>
> Likewise for these. the stm32-lptimer driver already checks that these
> are valid, which do you need to do it again?
>
> Well, technically you check for !NULL here, whereas stm32-lptimer does
> check for IS_ERR(), but neither regmap nor clk looks as though they're
> optional, and you won't ever get here if they can't be requested by
> stm32-lptimer in the first place.

You're right, I think I can simply drop this. (I kept this check from
pwm-stm32. Then I guess pmw-stm32 could be fixed as well.)

>
>> +
>> + priv->chip.base = -1;
>> + priv->chip.dev = &pdev->dev;
>> + priv->chip.ops = &stm32_pwm_lp_ops;
>> + priv->chip.npwm = 1;
>> +
>> + ret = pwmchip_add(&priv->chip);
>> + if (ret < 0)
>> + return ret;
>> +
>> + platform_set_drvdata(pdev, priv);
>> +
>> + return 0;
>> +}
>> +
>> +static int stm32_pwm_lp_remove(struct platform_device *pdev)
>> +{
>> + struct stm32_pwm_lp *priv = platform_get_drvdata(pdev);
>> +
>> + if (pwm_is_enabled(priv->chip.pwms))
>> + pwm_disable(priv->chip.pwms);
>
> It'd be better to use the more idiomatic variant for this:
>
> for (i = 0; i < priv->chip.npwm; i++)
> if (pwm_is_enabled(priv->chip.npwm))
> pwm_disable(&priv->chip.pwms[i]);
>
> That makes it easier to discern the common pattern and extract a helper,
> or move this to the core.

Ok, I'll update this in v3.

Many thanks for your careful review.
Best Regards,
Fabrice

>
> Thierry
>

2017-07-07 09:23:23

by Thierry Reding

[permalink] [raw]
Subject: Re: [PATCH v2 4/8] pwm: Add STM32 LPTimer PWM driver

On Fri, Jul 07, 2017 at 10:10:32AM +0200, Fabrice Gasnier wrote:
> On 07/06/2017 09:43 AM, Thierry Reding wrote:
> > On Wed, Jun 21, 2017 at 04:30:11PM +0200, Fabrice Gasnier wrote:
> >> Add support for single PWM channel on Low-Power Timer, that can be
> >> found on some STM32 platforms.
> >>
> >> Signed-off-by: Fabrice Gasnier <[email protected]>
> >> ---
> >> Changes in v2:
> >> - s/Low Power/Low-Power
> >> - update few comment lines
> >> ---
> >> drivers/pwm/Kconfig | 10 +++
> >> drivers/pwm/Makefile | 1 +
> >> drivers/pwm/pwm-stm32-lp.c | 216 +++++++++++++++++++++++++++++++++++++++++++++
> >> 3 files changed, 227 insertions(+)
> >> create mode 100644 drivers/pwm/pwm-stm32-lp.c
> >>
> >> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> >> index 313c107..7cb982b 100644
> >> --- a/drivers/pwm/Kconfig
> >> +++ b/drivers/pwm/Kconfig
> >> @@ -417,6 +417,16 @@ config PWM_STM32
> >> To compile this driver as a module, choose M here: the module
> >> will be called pwm-stm32.
> >>
> >> +config PWM_STM32_LP
> >> + tristate "STMicroelectronics STM32 PWM LP"
> >> + depends on MFD_STM32_LPTIMER || COMPILE_TEST
> >> + help
> >> + Generic PWM framework driver for STMicroelectronics STM32 SoCs
> >> + with Low-Power Timer (LPTIM).
> >> +
> >> + To compile this driver as a module, choose M here: the module
> >> + will be called pwm-stm32-lp.
> >> +
> >> config PWM_STMPE
> >> bool "STMPE expander PWM export"
> >> depends on MFD_STMPE
> >> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
> >> index 93da1f7..a3a4bee 100644
> >> --- a/drivers/pwm/Makefile
> >> +++ b/drivers/pwm/Makefile
> >> @@ -40,6 +40,7 @@ obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
> >> obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o
> >> obj-$(CONFIG_PWM_STI) += pwm-sti.o
> >> obj-$(CONFIG_PWM_STM32) += pwm-stm32.o
> >> +obj-$(CONFIG_PWM_STM32_LP) += pwm-stm32-lp.o
> >> obj-$(CONFIG_PWM_STMPE) += pwm-stmpe.o
> >> obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o
> >> obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o
> >> diff --git a/drivers/pwm/pwm-stm32-lp.c b/drivers/pwm/pwm-stm32-lp.c
> >> new file mode 100644
> >> index 0000000..eb997a8
> >> --- /dev/null
> >> +++ b/drivers/pwm/pwm-stm32-lp.c
> >> @@ -0,0 +1,216 @@
> >> +/*
> >> + * STM32 Low-Power Timer PWM driver
> >> + *
> >> + * Copyright (C) STMicroelectronics 2017
> >> + *
> >> + * Author: Gerald Baeza <[email protected]>
> >> + *
> >> + * License terms: GNU General Public License (GPL), version 2
> >> + *
> >> + * Inspired by Gerald Baeza's pwm-stm32 driver
> >> + */
> >> +
> >> +#include <linux/bitfield.h>
> >> +#include <linux/mfd/stm32-lptimer.h>
> >> +#include <linux/module.h>
> >> +#include <linux/of.h>
> >> +#include <linux/platform_device.h>
> >> +#include <linux/pwm.h>
> >> +
> >> +struct stm32_pwm_lp {
> >> + struct pwm_chip chip;
> >> + struct clk *clk;
> >> + struct regmap *regmap;
> >> +};
> >> +
> >> +static inline struct stm32_pwm_lp *to_stm32_pwm_lp(struct pwm_chip *chip)
> >> +{
> >> + return container_of(chip, struct stm32_pwm_lp, chip);
> >> +}
> >> +
> >> +static const u8 prescalers[] = {1, 2, 4, 8, 16, 32, 64, 128};
> >> +
> >> +static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm,
> >> + struct pwm_state *state)
> >> +{
> >> + struct stm32_pwm_lp *priv = to_stm32_pwm_lp(chip);
> >> + unsigned long long prd, div, dty;
> >> + struct pwm_state cstate;
> >> + u32 val, mask, cfgr, wavpol, presc = 0;
> >> + bool reenable = false;
> >> + int ret;
> >> +
> >> + pwm_get_state(pwm, &cstate);
> >> +
> >> + if (!state->enabled) {
> >> + if (cstate.enabled) {
> >> + /* Disable LP timer */
> >> + ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
> >> + if (ret)
> >> + return ret;
> >> + clk_disable(priv->clk);
> >> + }
> >> + return 0;
> >> + }
> >> +
> >> + /* Calculate the period and prescaler value */
> >> + div = (unsigned long long)clk_get_rate(priv->clk) * state->period;
> >> + do_div(div, NSEC_PER_SEC);
> >> + prd = div;
> >> + while (div > STM32_LPTIM_MAX_ARR) {
> >> + presc++;
> >> + if (presc >= ARRAY_SIZE(prescalers)) {
> >> + dev_err(priv->chip.dev, "max prescaler exceeded\n");
> >> + return -EINVAL;
> >> + }
> >> + div = prd;
> >> + do_div(div, prescalers[presc]);
> >> + }
> >> + prd = div;
> >> +
> >> + /* Calculate the duty cycle */
> >> + dty = prd * state->duty_cycle;
> >> + do_div(dty, state->period);
> >> +
> >> + wavpol = FIELD_PREP(STM32_LPTIM_WAVPOL, state->polarity);
> >> +
> >> + if (!cstate.enabled) {
> >> + ret = clk_enable(priv->clk);
> >> + if (ret)
> >> + return ret;
> >> + }
> >
> > Why do you need the checks here? Clock enabled are reference counted, so
> > you could do the clk_enable() unconditionally.
>
> Hi Thierry,
>
> This clock is used to generate PWM (source for LP timer counter). I
> enable it here as:
> - required state is 'enabled'
> - current state is 'disabled'.
> PWM is being turned on: first enable clock, then configure & enable PWM
> bellow.
>
> The opposite is done earlier, at the beginning of this routine:
> - required state is 'disabled'
> - current state is 'enabled'
> PWM is turned off, then clock is disabled.
>
> Enable count should be balanced, and clock is enabled when required
> (e.g. when PWM is 'on'). Doing it unconditionally here may cause
> unbalanced enable count (e.g. any duty_cycle update would increase
> enable count)
> Is it ok to keep this ?

The placement of the call suggested that you also need to enable the
clock in order to access any of the registers. In such cases it's often
simpler to take (and release) the reference to the clock irrespective
of the current state.

So the general sequence might look like this:

/* allow access to registers */
clk_enable();

/* modify registers */
...

/* enable clock to drive PWM counter */
if (state->enabled && !cstate.enabled)
clk_enable();

/* disable clock to PWM counter */
if (!state->enabled && cstate.enabled)
clk_disable();

/* access to registers no longer needed */
clk_disable();

This ensures that as long as you keep the "register" reference to the
clock, the clock will remain on.

There is a somewhat tricky situation that could happen if the initial
clock reference count is not in sync. Consider the case where your
->get_state() determines that the PWM is enabled. cstate.enabled would
be true, but the clock enable count would not be incremented. So you'd
have to make sure to add code to the ->get_state() implementation that
calls clk_enable() for each PWM that is enabled.

In my opinion that would be the cleanest option and easiest to follow,
but its more work to properly implement than I initially assumed, so
I'm fine with the version that you have, too.

> > Speaking of which, I don't see a clk_prepare() anywhere. Doesn't the clk
> > core warn about clk_enable() being called on a clock that's not been
> > prepared?
>
> clk_get() and clk_prepare() happens in regmap layer, when probing mfd part:
> -> stm32_lptimer_probe()
> -> devm_regmap_init_mmio_clk()
> -> __devm_regmap_init_mmio_clk()
> -> regmap_mmio_gen_context()

Okay, looks like we don't need it here, then.

> >> + (presc != FIELD_GET(STM32_LPTIM_PRESC, cfgr))) {
> >> + val = FIELD_PREP(STM32_LPTIM_PRESC, presc) | wavpol;
> >> + mask = STM32_LPTIM_PRESC | STM32_LPTIM_WAVPOL;
> >> +
> >> + /* Must disable LP timer to modify CFGR */
> >> + ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
> >> + if (ret)
> >> + goto err;
> >> + reenable = true;
> >
> > The placement of this is somewhat odd. It suggests that it is somehow
> > related to the disabling of the LP timer, whereas it really isn't.
>
> In case of prescaler or polarity change, CFGR register needs to be
> updated. CFGR register must be modified only when LP timer HW is disabled.
> - Initial choice is to use this flag, to temporarily disable HW, update
> cfgr, then re-enable it. More thinking about this...

What I find odd about the placement is that it is between the
regmap_write() and the regmap_update_bits(). But it could just as well
be after the regmap_update_bits() (or before regmap_write() for that
matter). So the confusing thing is why it "breaks" the sequence of
register accesses.

> - Another choice could be to refuse such a 'live' change and report
> (busy?) error ? Then user would have to explicitly disable it, configure
> new setting and re-enable it.
>
> Please let me know your opinion.

The PWM subsystem doesn't give a guarantee that a live change is
possible. Drivers always have to assume that the PWM may get disabled
and reenabled as part of the sequence.

That said, something like this could be added in the future if users
come along that required this guarantee.

For your driver, I think it's fine to keep this as-is.

> >> +static int stm32_pwm_lp_probe(struct platform_device *pdev)
> >> +{
> >> + struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
> >> + struct stm32_pwm_lp *priv;
> >> + int ret;
> >> +
> >> + if (IS_ERR_OR_NULL(ddata))
> >> + return -EINVAL;
> >
> > It seems to me like this can never happen. How would you trigger this
> > condition?
>
> Bad dt configuration can trigger this error: thinking of a
> 'st,stm32-pwm-lp' dt node without proper mfd parent. Do you want me to
> drop this ?
> (or add comment about it ?)

In my opinion we should trust DTB in this type of situation. If the DT
binding says that the PWM node needs to be a child of an MFD, then the
author of the DTB needs to make sure it is.

For things that can be easily checked I think it makes sense to validate
the DTB, but this check here is not enough for all situations, right?
What if somebody added the device node as child to some unrelated node.
ddata could be a valid pointer, but pointing at something that's not a
struct stm32_lptimer at all. That's still pretty bad, and completely
undetectable.

Thierry


Attachments:
(No filename) (9.68 kB)
signature.asc (833.00 B)
Download all attachments

2017-07-07 10:11:51

by Fabrice Gasnier

[permalink] [raw]
Subject: Re: [PATCH v2 4/8] pwm: Add STM32 LPTimer PWM driver

On 07/07/2017 11:23 AM, Thierry Reding wrote:
> On Fri, Jul 07, 2017 at 10:10:32AM +0200, Fabrice Gasnier wrote:
>> On 07/06/2017 09:43 AM, Thierry Reding wrote:
>>> On Wed, Jun 21, 2017 at 04:30:11PM +0200, Fabrice Gasnier wrote:
>>>> Add support for single PWM channel on Low-Power Timer, that can be
>>>> found on some STM32 platforms.
>>>>
>>>> Signed-off-by: Fabrice Gasnier <[email protected]>
>>>> ---
>>>> Changes in v2:
>>>> - s/Low Power/Low-Power
>>>> - update few comment lines
>>>> ---
>>>> drivers/pwm/Kconfig | 10 +++
>>>> drivers/pwm/Makefile | 1 +
>>>> drivers/pwm/pwm-stm32-lp.c | 216 +++++++++++++++++++++++++++++++++++++++++++++
>>>> 3 files changed, 227 insertions(+)
>>>> create mode 100644 drivers/pwm/pwm-stm32-lp.c
>>>>
>>>> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
>>>> index 313c107..7cb982b 100644
>>>> --- a/drivers/pwm/Kconfig
>>>> +++ b/drivers/pwm/Kconfig
>>>> @@ -417,6 +417,16 @@ config PWM_STM32
>>>> To compile this driver as a module, choose M here: the module
>>>> will be called pwm-stm32.
>>>>
>>>> +config PWM_STM32_LP
>>>> + tristate "STMicroelectronics STM32 PWM LP"
>>>> + depends on MFD_STM32_LPTIMER || COMPILE_TEST
>>>> + help
>>>> + Generic PWM framework driver for STMicroelectronics STM32 SoCs
>>>> + with Low-Power Timer (LPTIM).
>>>> +
>>>> + To compile this driver as a module, choose M here: the module
>>>> + will be called pwm-stm32-lp.
>>>> +
>>>> config PWM_STMPE
>>>> bool "STMPE expander PWM export"
>>>> depends on MFD_STMPE
>>>> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
>>>> index 93da1f7..a3a4bee 100644
>>>> --- a/drivers/pwm/Makefile
>>>> +++ b/drivers/pwm/Makefile
>>>> @@ -40,6 +40,7 @@ obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
>>>> obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o
>>>> obj-$(CONFIG_PWM_STI) += pwm-sti.o
>>>> obj-$(CONFIG_PWM_STM32) += pwm-stm32.o
>>>> +obj-$(CONFIG_PWM_STM32_LP) += pwm-stm32-lp.o
>>>> obj-$(CONFIG_PWM_STMPE) += pwm-stmpe.o
>>>> obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o
>>>> obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o
>>>> diff --git a/drivers/pwm/pwm-stm32-lp.c b/drivers/pwm/pwm-stm32-lp.c
>>>> new file mode 100644
>>>> index 0000000..eb997a8
>>>> --- /dev/null
>>>> +++ b/drivers/pwm/pwm-stm32-lp.c
>>>> @@ -0,0 +1,216 @@
>>>> +/*
>>>> + * STM32 Low-Power Timer PWM driver
>>>> + *
>>>> + * Copyright (C) STMicroelectronics 2017
>>>> + *
>>>> + * Author: Gerald Baeza <[email protected]>
>>>> + *
>>>> + * License terms: GNU General Public License (GPL), version 2
>>>> + *
>>>> + * Inspired by Gerald Baeza's pwm-stm32 driver
>>>> + */
>>>> +
>>>> +#include <linux/bitfield.h>
>>>> +#include <linux/mfd/stm32-lptimer.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/of.h>
>>>> +#include <linux/platform_device.h>
>>>> +#include <linux/pwm.h>
>>>> +
>>>> +struct stm32_pwm_lp {
>>>> + struct pwm_chip chip;
>>>> + struct clk *clk;
>>>> + struct regmap *regmap;
>>>> +};
>>>> +
>>>> +static inline struct stm32_pwm_lp *to_stm32_pwm_lp(struct pwm_chip *chip)
>>>> +{
>>>> + return container_of(chip, struct stm32_pwm_lp, chip);
>>>> +}
>>>> +
>>>> +static const u8 prescalers[] = {1, 2, 4, 8, 16, 32, 64, 128};
>>>> +
>>>> +static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm,
>>>> + struct pwm_state *state)
>>>> +{
>>>> + struct stm32_pwm_lp *priv = to_stm32_pwm_lp(chip);
>>>> + unsigned long long prd, div, dty;
>>>> + struct pwm_state cstate;
>>>> + u32 val, mask, cfgr, wavpol, presc = 0;
>>>> + bool reenable = false;
>>>> + int ret;
>>>> +
>>>> + pwm_get_state(pwm, &cstate);
>>>> +
>>>> + if (!state->enabled) {
>>>> + if (cstate.enabled) {
>>>> + /* Disable LP timer */
>>>> + ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
>>>> + if (ret)
>>>> + return ret;
>>>> + clk_disable(priv->clk);
>>>> + }
>>>> + return 0;
>>>> + }
>>>> +
>>>> + /* Calculate the period and prescaler value */
>>>> + div = (unsigned long long)clk_get_rate(priv->clk) * state->period;
>>>> + do_div(div, NSEC_PER_SEC);
>>>> + prd = div;
>>>> + while (div > STM32_LPTIM_MAX_ARR) {
>>>> + presc++;
>>>> + if (presc >= ARRAY_SIZE(prescalers)) {
>>>> + dev_err(priv->chip.dev, "max prescaler exceeded\n");
>>>> + return -EINVAL;
>>>> + }
>>>> + div = prd;
>>>> + do_div(div, prescalers[presc]);
>>>> + }
>>>> + prd = div;
>>>> +
>>>> + /* Calculate the duty cycle */
>>>> + dty = prd * state->duty_cycle;
>>>> + do_div(dty, state->period);
>>>> +
>>>> + wavpol = FIELD_PREP(STM32_LPTIM_WAVPOL, state->polarity);
>>>> +
>>>> + if (!cstate.enabled) {
>>>> + ret = clk_enable(priv->clk);
>>>> + if (ret)
>>>> + return ret;
>>>> + }
>>>
>>> Why do you need the checks here? Clock enabled are reference counted, so
>>> you could do the clk_enable() unconditionally.
>>
>> Hi Thierry,
>>
>> This clock is used to generate PWM (source for LP timer counter). I
>> enable it here as:
>> - required state is 'enabled'
>> - current state is 'disabled'.
>> PWM is being turned on: first enable clock, then configure & enable PWM
>> bellow.
>>
>> The opposite is done earlier, at the beginning of this routine:
>> - required state is 'disabled'
>> - current state is 'enabled'
>> PWM is turned off, then clock is disabled.
>>
>> Enable count should be balanced, and clock is enabled when required
>> (e.g. when PWM is 'on'). Doing it unconditionally here may cause
>> unbalanced enable count (e.g. any duty_cycle update would increase
>> enable count)
>> Is it ok to keep this ?
>
> The placement of the call suggested that you also need to enable the
> clock in order to access any of the registers. In such cases it's often
> simpler to take (and release) the reference to the clock irrespective
> of the current state.
>
> So the general sequence might look like this:
>
> /* allow access to registers */
> clk_enable();
>
> /* modify registers */
> ...
>
> /* enable clock to drive PWM counter */
> if (state->enabled && !cstate.enabled)
> clk_enable();
>
> /* disable clock to PWM counter */
> if (!state->enabled && cstate.enabled)
> clk_disable();
>
> /* access to registers no longer needed */
> clk_disable();
>
> This ensures that as long as you keep the "register" reference to the
> clock, the clock will remain on.

Hi Thierry,

I better see your point now. Regmap is already handling clock for
register access. I'll try to re-arrange things a little, so it's easier
to read and comment about enable/disable clock to PWM counter.

>
> There is a somewhat tricky situation that could happen if the initial
> clock reference count is not in sync. Consider the case where your
> ->get_state() determines that the PWM is enabled. cstate.enabled would
> be true, but the clock enable count would not be incremented. So you'd
> have to make sure to add code to the ->get_state() implementation that
> calls clk_enable() for each PWM that is enabled.
>
> In my opinion that would be the cleanest option and easiest to follow,
> but its more work to properly implement than I initially assumed, so
> I'm fine with the version that you have, too.

Thanks for these hints, I'll try to follow your advise on this:
- use get_state()
- call clk_enable() if PWM is already enabled.

>
>>> Speaking of which, I don't see a clk_prepare() anywhere. Doesn't the clk
>>> core warn about clk_enable() being called on a clock that's not been
>>> prepared?
>>
>> clk_get() and clk_prepare() happens in regmap layer, when probing mfd part:
>> -> stm32_lptimer_probe()
>> -> devm_regmap_init_mmio_clk()
>> -> __devm_regmap_init_mmio_clk()
>> -> regmap_mmio_gen_context()
>
> Okay, looks like we don't need it here, then.
>
>>>> + (presc != FIELD_GET(STM32_LPTIM_PRESC, cfgr))) {
>>>> + val = FIELD_PREP(STM32_LPTIM_PRESC, presc) | wavpol;
>>>> + mask = STM32_LPTIM_PRESC | STM32_LPTIM_WAVPOL;
>>>> +
>>>> + /* Must disable LP timer to modify CFGR */
>>>> + ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
>>>> + if (ret)
>>>> + goto err;
>>>> + reenable = true;
>>>
>>> The placement of this is somewhat odd. It suggests that it is somehow
>>> related to the disabling of the LP timer, whereas it really isn't.
>>
>> In case of prescaler or polarity change, CFGR register needs to be
>> updated. CFGR register must be modified only when LP timer HW is disabled.
>> - Initial choice is to use this flag, to temporarily disable HW, update
>> cfgr, then re-enable it. More thinking about this...
>
> What I find odd about the placement is that it is between the
> regmap_write() and the regmap_update_bits(). But it could just as well
> be after the regmap_update_bits() (or before regmap_write() for that
> matter). So the confusing thing is why it "breaks" the sequence of
> register accesses.

Ok, I'll move it before or after register accesses sequence.

>
>> - Another choice could be to refuse such a 'live' change and report
>> (busy?) error ? Then user would have to explicitly disable it, configure
>> new setting and re-enable it.
>>
>> Please let me know your opinion.
>
> The PWM subsystem doesn't give a guarantee that a live change is
> possible. Drivers always have to assume that the PWM may get disabled
> and reenabled as part of the sequence.
>
> That said, something like this could be added in the future if users
> come along that required this guarantee.
>
> For your driver, I think it's fine to keep this as-is.
Got it.

>
>>>> +static int stm32_pwm_lp_probe(struct platform_device *pdev)
>>>> +{
>>>> + struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
>>>> + struct stm32_pwm_lp *priv;
>>>> + int ret;
>>>> +
>>>> + if (IS_ERR_OR_NULL(ddata))
>>>> + return -EINVAL;
>>>
>>> It seems to me like this can never happen. How would you trigger this
>>> condition?
>>
>> Bad dt configuration can trigger this error: thinking of a
>> 'st,stm32-pwm-lp' dt node without proper mfd parent. Do you want me to
>> drop this ?
>> (or add comment about it ?)
>
> In my opinion we should trust DTB in this type of situation. If the DT
> binding says that the PWM node needs to be a child of an MFD, then the
> author of the DTB needs to make sure it is.
>
> For things that can be easily checked I think it makes sense to validate
> the DTB, but this check here is not enough for all situations, right?
> What if somebody added the device node as child to some unrelated node.
> ddata could be a valid pointer, but pointing at something that's not a
> struct stm32_lptimer at all. That's still pretty bad, and completely
> undetectable.

You're right. I'll remove this as well.

Thanks again,
Best Regards,
Fabrice

>
> Thierry
>