2017-06-16 16:03:04

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH 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,
reset) 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

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 | 25 ++
.../bindings/iio/timer/stm32-lptimer-trigger.txt | 21 ++
.../devicetree/bindings/mfd/stm32-lptimer.txt | 51 +++
.../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 | 382 +++++++++++++++++++++
drivers/iio/trigger/Kconfig | 11 +
drivers/iio/trigger/Makefile | 1 +
drivers/iio/trigger/stm32-lptimer-trigger.c | 110 ++++++
drivers/mfd/Kconfig | 15 +
drivers/mfd/Makefile | 1 +
drivers/mfd/stm32-lptimer.c | 110 ++++++
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 | 55 +++
19 files changed, 1124 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-16 16:00:11

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH 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]>
---
.../devicetree/bindings/mfd/stm32-lptimer.txt | 51 ++++++++++++++++++++++
1 file changed, 51 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..237b7d6
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
@@ -0,0 +1,51 @@
+STMicroelectronics STM32 Low Power Timer
+
+The STM32 Low Power Timer (LPTIM) is a 16-bit timer that provides several
+functionalities:
+- 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 "int".
+- #address-cells = <1>;
+- #size-cells = <0>;
+
+Optional properties:
+- resets: Must contain the phandle to the reset controller.
+
+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@0x40002400 {
+ compatible = "st,stm32-lptimer";
+ reg = <0x40002400 0x400>;
+ clocks = <&timer_clk>;
+ clock-names = "int";
+ #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-16 16:00:14

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH 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]>
---
drivers/mfd/Kconfig | 15 ++++++
drivers/mfd/Makefile | 1 +
drivers/mfd/stm32-lptimer.c | 110 ++++++++++++++++++++++++++++++++++++++
include/linux/mfd/stm32-lptimer.h | 55 +++++++++++++++++++
4 files changed, 181 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..32c1f3f 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1683,6 +1683,21 @@ 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
+ select RESET_CONTROLLER
+ help
+ Select this option to enable STM32 low power timer driver
+ used for PWM, IIO trigger, IIO encoder and counter. This driver
+ allows to share the resources between these drivers.
+
+ 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..fcfefa3
--- /dev/null
+++ b/drivers/mfd/stm32-lptimer.c
@@ -0,0 +1,110 @@
+/*
+ * This file is part of STM32 low-power timer driver
+ *
+ * Copyright (C) STMicroelectronics 2017
+ *
+ * Author: Fabrice Gasnier <[email protected]>
+ * Inspired from: stm32-timers from Benjamin Gaignard
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/mfd/stm32-lptimer.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/reset.h>
+
+static const struct regmap_config stm32_lptimer_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = sizeof(u32),
+ .max_register = 0x3fc,
+};
+
+static int stm32_lptimer_detect_encoder(struct stm32_lptimer *ddata)
+{
+ u32 val, enc = STM32_LPTIM_ENC;
+ int ret;
+
+ /*
+ * Quadrature encoder mode bit can only be written and read back when
+ * LP Timer supports is.
+ */
+ ret = regmap_update_bits(ddata->regmap, STM32_LPTIM_CFGR, enc, 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, enc, 0);
+ if (ret)
+ return ret;
+ ddata->has_encoder = !!val;
+
+ return 0;
+}
+
+static int stm32_lptimer_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct stm32_lptimer *ddata;
+ struct reset_control *rst;
+ 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, "int", 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);
+
+ rst = devm_reset_control_get_optional(dev, NULL);
+ if (IS_ERR(rst))
+ return PTR_ERR(rst);
+
+ if (rst) {
+ reset_control_assert(rst);
+ reset_control_deassert(rst);
+ }
+
+ 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", },
+ { /* end node */ },
+};
+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..72bc9be
--- /dev/null
+++ b/include/linux/mfd/stm32-lptimer.h
@@ -0,0 +1,55 @@
+/*
+ * This file is part of STM32 low-power timer driver
+ *
+ * Copyright (C) STMicroelectronics 2016
+ *
+ * Author: Fabrice Gasnier <[email protected]>
+ * Inspired from: stm32-timers
+ *
+ * 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 {
+ struct clk *clk;
+ struct regmap *regmap;
+ bool has_encoder;
+};
+
+#endif
--
1.9.1

2017-06-16 16:00:29

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH 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]>
---
.../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 | 382 +++++++++++++++++++++
4 files changed, 449 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..c209779 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..b03bc9e
--- /dev/null
+++ b/drivers/iio/counter/stm32-lptimer-cnt.c
@@ -0,0 +1,382 @@
+/*
+ * This file is part of STM32 low-power timer driver
+ *
+ * Copyright (C) STMicroelectronics 2017
+ *
+ * Author: Fabrice Gasnier <[email protected]>
+ * Inspired from: 104-quad-8 and stm32-timer-trigger
+ *
+ * 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", },
+ { /* end node */ },
+};
+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-16 16:00:52

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH 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]>
---
.../bindings/iio/timer/stm32-lptimer-trigger.txt | 21 +++++++++++++++++++++
1 file changed, 21 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..6ece007
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt
@@ -0,0 +1,21 @@
+STMicroelectronics STM32 Low Power Timer trigger
+
+STM32 LP 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 LP 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: Identify trigger hardware block.
+
+Example:
+ lptimer1: lptimer@0x40002400 {
+ compatible = "st,stm32-lptimer";
+ ...
+ trigger@0 {
+ compatible = "st,stm32-lptimer-trigger";
+ reg = <0>;
+ };
+ };
--
1.9.1

2017-06-16 16:00:09

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH 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]>
---
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..6ac4477 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..0de1256
--- /dev/null
+++ b/drivers/pwm/pwm-stm32-lp.c
@@ -0,0 +1,216 @@
+/*
+ * This file is part of STM32 low-power timer driver
+ *
+ * Copyright (C) STMicroelectronics 2017
+ *
+ * Author: Gerald Baeza <[email protected]>
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * Inspired by pwm-stm32.c from Gerald Baeza
+ */
+
+#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", },
+ { /* end node */ },
+};
+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-16 16:01:12

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH 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]>
---
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..66823bd 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 LP 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..75bbaf2
--- /dev/null
+++ b/drivers/iio/trigger/stm32-lptimer-trigger.c
@@ -0,0 +1,110 @@
+/*
+ * This file is part of STM32 low-power timer driver
+ *
+ * Copyright (C) STMicroelectronics 2017
+ *
+ * Author: Fabrice Gasnier <[email protected]>.
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * Inspired by stm32-timer-trigger.c from Benjamin Gaignard
+ */
+
+#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 the triggers created by each low-power timer */
+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", },
+ { /* end node */ },
+};
+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-16 16:01:48

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH 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]>
---
.../bindings/iio/counter/stm32-lptimer-cnt.txt | 25 ++++++++++++++++++++++
1 file changed, 25 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..e31e54d
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/counter/stm32-lptimer-cnt.txt
@@ -0,0 +1,25 @@
+STMicroelectronics STM32 Low Power Timer quadrature encoder and counter
+
+STM32 LP 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 LP Timer device tree node.
+See ../mfd/stm32-lptimer.txt for details about the parent node.
+
+Required properties:
+- compatible: Must be "st,stm32-lptimer-counter".
+- A pinctrl state named "default" can be defined to set IN1/IN2 pins in mode
+ of operation for LP Timer input signals on external pin.
+
+Example:
+ lptimer1: lptimer@0x40002400 {
+ compatible = "st,stm32-lptimer";
+ ...
+ counter {
+ compatible = "st,stm32-lptimer-counter";
+ pinctrl-names = "default";
+ pinctrl-0 = <&lptim1_in_pins>;
+ };
+ };
--
1.9.1

2017-06-16 16:02:03

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH 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]>
---
.../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..8eebe31
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/pwm-stm32-lp.txt
@@ -0,0 +1,24 @@
+STMicroelectronics STM32 Low Power Timer PWM
+
+STM32 LP Timer provides single channel PWM.
+
+Must be a sub-node of an STM32 LP 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:
+- A pinctrl state named "default" can be defined to set a pin in mode of
+ operation for PWM output on external pin.
+
+Example:
+ lptimer1: lptimer@0x40002400 {
+ compatible = "st,stm32-lptimer";
+ ...
+ pwm {
+ compatible = "st,stm32-pwm-lp";
+ pinctrl-names = "default";
+ pinctrl-0 = <&lppwm1_pins>;
+ };
+ };
--
1.9.1

2017-06-19 15:51:18

by Lee Jones

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

On Fri, 16 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]>
> ---
> drivers/mfd/Kconfig | 15 ++++++
> drivers/mfd/Makefile | 1 +
> drivers/mfd/stm32-lptimer.c | 110 ++++++++++++++++++++++++++++++++++++++
> include/linux/mfd/stm32-lptimer.h | 55 +++++++++++++++++++
> 4 files changed, 181 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..32c1f3f 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -1683,6 +1683,21 @@ config MFD_STW481X
> in various ST Microelectronics and ST-Ericsson embedded
> Nomadik series.
>
> +config MFD_STM32_LPTIMER
> + tristate "Support for STM32 low power timer"

It think this should be "Low Power Timer" throughout.

Or even "Low-Power Timer". You decide.

> + depends on (ARCH_STM32 && OF) || COMPILE_TEST
> + select MFD_CORE
> + select REGMAP
> + select REGMAP_MMIO
> + select RESET_CONTROLLER
> + help
> + Select this option to enable STM32 low power timer driver
> + used for PWM, IIO trigger, IIO encoder and counter. This driver

"Counter"

> + allows to share the resources between these drivers.

"Shared resources are also dealt with here." Or similar.

> + 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..fcfefa3
> --- /dev/null
> +++ b/drivers/mfd/stm32-lptimer.c
> @@ -0,0 +1,110 @@
> +/*
> + * This file is part of STM32 low-power timer driver

This is a separate driver. It isn't part of anything.

"STM32 Low Power Timer parent driver".

> + * Copyright (C) STMicroelectronics 2017
> + *
> + * Author: Fabrice Gasnier <[email protected]>

'\n' here.

> + * Inspired from: stm32-timers from Benjamin Gaignard

"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>
> +#include <linux/reset.h>
> +
> +static const struct regmap_config stm32_lptimer_regmap_cfg = {
> + .reg_bits = 32,
> + .val_bits = 32,
> + .reg_stride = sizeof(u32),
> + .max_register = 0x3fc,

This should be defined.

> +};
> +
> +static int stm32_lptimer_detect_encoder(struct stm32_lptimer *ddata)
> +{
> + u32 val, enc = STM32_LPTIM_ENC;

Why have enc? Why not just use STM32_LPTIM_ENC?

> + int ret;
> +
> + /*
> + * Quadrature encoder mode bit can only be written and read back when
> + * LP Timer supports is.

"it"

> + */
> + ret = regmap_update_bits(ddata->regmap, STM32_LPTIM_CFGR, enc, enc);
> + if (ret)
> + return ret;

'\n' here.

> + ret = regmap_read(ddata->regmap, STM32_LPTIM_CFGR, &val);
> + if (ret)
> + return ret;

'\n' here.

> + ret = regmap_update_bits(ddata->regmap, STM32_LPTIM_CFGR, enc, 0);
> + if (ret)
> + return ret;

'\n' here.

> + ddata->has_encoder = !!val;
> +
> + return 0;
> +}
> +
> +static int stm32_lptimer_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct stm32_lptimer *ddata;
> + struct reset_control *rst;

This looks quite a lot like 'ret'. Just use 'reset'.

> + 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, "int", mmio,

Is that what the clock is called in the datasheet?

> + &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);
> +
> + rst = devm_reset_control_get_optional(dev, NULL);
> + if (IS_ERR(rst))
> + return PTR_ERR(rst);
> +
> + if (rst) {
> + reset_control_assert(rst);
> + reset_control_deassert(rst);
> + }
This is odd. Please provide a comment as to why you're doing this.

> + 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", },
> + { /* end node */ },

No need for the comment.

> +};
> +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..72bc9be
> --- /dev/null
> +++ b/include/linux/mfd/stm32-lptimer.h
> @@ -0,0 +1,55 @@
> +/*
> + * This file is part of STM32 low-power timer driver
> + *
> + * Copyright (C) STMicroelectronics 2016
> + *
> + * Author: Fabrice Gasnier <[email protected]>
> + * Inspired from: stm32-timers

Same as the comments I provided for the *.c file above.

> + * 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 {
> + struct clk *clk;
> + struct regmap *regmap;
> + bool has_encoder;
> +};

Please provide a kerneldoc type header for this struct.

> +#endif

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

2017-06-20 11:50:47

by Lee Jones

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

On Fri, 16 Jun 2017, Fabrice Gasnier wrote:

> Add documentation for STMicroelectronics STM32 Low Power Timer binding.
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
> ---
> .../devicetree/bindings/mfd/stm32-lptimer.txt | 51 ++++++++++++++++++++++
> 1 file changed, 51 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..237b7d6
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
> @@ -0,0 +1,51 @@
> +STMicroelectronics STM32 Low Power Timer
> +
> +The STM32 Low Power Timer (LPTIM) is a 16-bit timer that provides several
> +functionalities:

"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 "int".

These look better when padded:

- 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 "int".

> +- #address-cells = <1>;
> +- #size-cells = <0>;

Please use the same format as you did for the other properties.

> +Optional properties:
> +- resets: Must contain the phandle to the reset controller.
> +
> +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@0x40002400 {

Remove '0x'

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

Why is this @0 and no reg properties are provided for the other 2
nodes?

> + 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-20 15:52:40

by Fabrice Gasnier

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

On 06/19/2017 05:51 PM, Lee Jones wrote:
> On Fri, 16 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]>
>> ---
>> drivers/mfd/Kconfig | 15 ++++++
>> drivers/mfd/Makefile | 1 +
>> drivers/mfd/stm32-lptimer.c | 110 ++++++++++++++++++++++++++++++++++++++
>> include/linux/mfd/stm32-lptimer.h | 55 +++++++++++++++++++
>> 4 files changed, 181 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..32c1f3f 100644
>> --- a/drivers/mfd/Kconfig
>> +++ b/drivers/mfd/Kconfig
>> @@ -1683,6 +1683,21 @@ config MFD_STW481X
>> in various ST Microelectronics and ST-Ericsson embedded
>> Nomadik series.
>>
>> +config MFD_STM32_LPTIMER
>> + tristate "Support for STM32 low power timer"
>
> It think this should be "Low Power Timer" throughout.
>
> Or even "Low-Power Timer". You decide.

Hi Lee,

I'll pick your second proposal that also matches with reference manuals.

>
>> + depends on (ARCH_STM32 && OF) || COMPILE_TEST
>> + select MFD_CORE
>> + select REGMAP
>> + select REGMAP_MMIO
>> + select RESET_CONTROLLER
>> + help
>> + Select this option to enable STM32 low power timer driver
>> + used for PWM, IIO trigger, IIO encoder and counter. This driver
>
> "Counter"
Do you mean capital 'C' for counter (only) ?
Shall I also change "trigger" and "encoder" with capital letters too ?

>
>> + allows to share the resources between these drivers.
>
> "Shared resources are also dealt with here." Or similar.
I'll fix it in v2.

>
>> + 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..fcfefa3
>> --- /dev/null
>> +++ b/drivers/mfd/stm32-lptimer.c
>> @@ -0,0 +1,110 @@
>> +/*
>> + * This file is part of STM32 low-power timer driver
>
> This is a separate driver. It isn't part of anything.
>
> "STM32 Low Power Timer parent driver".
I'll fix it in v2.
>
>> + * Copyright (C) STMicroelectronics 2017
>> + *
>> + * Author: Fabrice Gasnier <[email protected]>
>
> '\n' here.
I'll fix all missing lines in v2.
>
>> + * Inspired from: stm32-timers from Benjamin Gaignard
>
> "Inspired by Benjamin Gaignard's stm32-timers driver"
I'll fix it in v2.
>
>> + * License terms: GNU General Public License (GPL), version 2
>> + */
>> +
>> +#include <linux/mfd/stm32-lptimer.h>
>> +#include <linux/module.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/reset.h>
>> +
>> +static const struct regmap_config stm32_lptimer_regmap_cfg = {
>> + .reg_bits = 32,
>> + .val_bits = 32,
>> + .reg_stride = sizeof(u32),
>> + .max_register = 0x3fc,
>
> This should be defined.
I'll fix it in v2.
>
>> +};
>> +
>> +static int stm32_lptimer_detect_encoder(struct stm32_lptimer *ddata)
>> +{
>> + u32 val, enc = STM32_LPTIM_ENC;
>
> Why have enc? Why not just use STM32_LPTIM_ENC?

This only allows to spare few lines bellow.
But I can remove it and use STM32_LPTIM_ENC instead.
Do you prefer I do this ?

>
>> + int ret;
>> +
>> + /*
>> + * Quadrature encoder mode bit can only be written and read back when
>> + * LP Timer supports is.
>
> "it"
>
>> + */
>> + ret = regmap_update_bits(ddata->regmap, STM32_LPTIM_CFGR, enc, enc);
>> + if (ret)
>> + return ret;
>
> '\n' here.
>
>> + ret = regmap_read(ddata->regmap, STM32_LPTIM_CFGR, &val);
>> + if (ret)
>> + return ret;
>
> '\n' here.
>
>> + ret = regmap_update_bits(ddata->regmap, STM32_LPTIM_CFGR, enc, 0);
>> + if (ret)
>> + return ret;
>
> '\n' here.
>
>> + ddata->has_encoder = !!val;
>> +
>> + return 0;
>> +}
>> +
>> +static int stm32_lptimer_probe(struct platform_device *pdev)
>> +{
>> + struct device *dev = &pdev->dev;
>> + struct stm32_lptimer *ddata;
>> + struct reset_control *rst;
>
> This looks quite a lot like 'ret'. Just use 'reset'.
I'll fix it in v2.
>
>> + 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, "int", mmio,
>
> Is that what the clock is called in the datasheet?

My mistake, this isn't clock name from the datasheet.
I kept same name from stm32-timers. I'll update this in v2.
In STM32H7 Reference Manual (RM0433) for, "Kernel clock distribution for
LPTIMs..." figure shows "CLKMUX" name for all LPTIM clock inputs.
It's not shown on STM32F7 (RM0385), only "low-power timer 1 clock".

Do you agree to call it "mux" here ?

>
>> + &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);
>> +
>> + rst = devm_reset_control_get_optional(dev, NULL);
>> + if (IS_ERR(rst))
>> + return PTR_ERR(rst);
>> +
>> + if (rst) {
>> + reset_control_assert(rst);
>> + reset_control_deassert(rst);
>> + }
> This is odd. Please provide a comment as to why you're doing this.

Yes, I'll remove it in next patchset (test purpose). It has already been
reset before boot.

>
>> + 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", },
>> + { /* end node */ },
>
> No need for the comment.
I'll fix it in v2.
>
>> +};
>> +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..72bc9be
>> --- /dev/null
>> +++ b/include/linux/mfd/stm32-lptimer.h
>> @@ -0,0 +1,55 @@
>> +/*
>> + * This file is part of STM32 low-power timer driver
>> + *
>> + * Copyright (C) STMicroelectronics 2016
>> + *
>> + * Author: Fabrice Gasnier <[email protected]>
>> + * Inspired from: stm32-timers
>
> Same as the comments I provided for the *.c file above.
I'll fix it in v2.
>
>> + * 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 {
>> + struct clk *clk;
>> + struct regmap *regmap;
>> + bool has_encoder;
>> +};
>
> Please provide a kerneldoc type header for this struct.

I'll fix it in v2.

Thanks for your review,
Best Regards,
Fabrice

>
>> +#endif
>

2017-06-20 16:07:26

by Fabrice Gasnier

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

On 06/20/2017 01:50 PM, Lee Jones wrote:
> On Fri, 16 Jun 2017, Fabrice Gasnier wrote:
>
>> Add documentation for STMicroelectronics STM32 Low Power Timer binding.
>>
>> Signed-off-by: Fabrice Gasnier <[email protected]>
>> ---
>> .../devicetree/bindings/mfd/stm32-lptimer.txt | 51 ++++++++++++++++++++++
>> 1 file changed, 51 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..237b7d6
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/mfd/stm32-lptimer.txt
>> @@ -0,0 +1,51 @@
>> +STMicroelectronics STM32 Low Power Timer
>> +
>> +The STM32 Low Power Timer (LPTIM) is a 16-bit timer that provides several
>> +functionalities:
>
> "functions".

Hi Lee,

I'll fix it in v2.
>
>> +- 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 "int".
>
> These look better when padded:
>
> - 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 "int".
>
>> +- #address-cells = <1>;
>> +- #size-cells = <0>;
>
> Please use the same format as you did for the other properties.

I'll fix it in v2.

>
>> +Optional properties:
>> +- resets: Must contain the phandle to the reset controller.
>> +
>> +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@0x40002400 {
>
> Remove '0x'
nice shot :-)

>
>> + compatible = "st,stm32-lptimer";
>> + reg = <0x40002400 0x400>;
>> + clocks = <&timer_clk>;
>> + clock-names = "int";
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> +
>> + pwm {
>> + compatible = "st,stm32-pwm-lp";
>> + pinctrl-names = "default";
>> + pinctrl-0 = <&lppwm1_pins>;
>> + };
>> +
>> + trigger@0 {
>
> Why is this @0 and no reg properties are provided for the other 2
> nodes?

This is to select each LPTimer configuration (similar to stm32-timers).
All LPTimers that provide output as trigger for ADC and/or DAC are the
same, except for which hardware signals they're connected to, and send
to ADC and/or DAC.
"reg" is used as index for trigger table in trigger driver code (PATCH
6): it must be 0 for lptimer1 output, 1 for lptimer2 output or 2 for
lptimer3 output.
I should probably document it in stm32-lptimer-trigger dt-bindings ?

This is not needed for other two nodes.

Thanks for reviewing,
Best Regards,
Fabrice

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