Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1667856 > unrolled thread
| Started by | Fabrice Gasnier <fabrice.gasnier@st.com> |
|---|---|
| First post | 2017-06-16 18:10 +0200 |
| Last post | 2017-06-16 18:10 +0200 |
| Articles | 6 — 2 participants |
Back to article view | Back to linux.kernel
[PATCH 0/8] Add STM32 LPTimer: PWM, trigger and counter Fabrice Gasnier <fabrice.gasnier@st.com> - 2017-06-16 18:10 +0200
[PATCH 5/8] dt-bindings: iio: Add STM32 LPTimer trigger binding Fabrice Gasnier <fabrice.gasnier@st.com> - 2017-06-16 18:10 +0200
[PATCH 8/8] iio: counter: Add support for STM32 LPTimer Fabrice Gasnier <fabrice.gasnier@st.com> - 2017-06-16 18:10 +0200
[PATCH 2/8] mfd: Add STM32 LPTimer driver Fabrice Gasnier <fabrice.gasnier@st.com> - 2017-06-16 18:10 +0200
Re: [PATCH 2/8] mfd: Add STM32 LPTimer driver Lee Jones <lee.jones@linaro.org> - 2017-06-19 18:00 +0200
[PATCH 3/8] dt-bindings: pwm: Add STM32 LPTimer PWM binding Fabrice Gasnier <fabrice.gasnier@st.com> - 2017-06-16 18:10 +0200
| From | Fabrice Gasnier <fabrice.gasnier@st.com> |
|---|---|
| Date | 2017-06-16 18:10 +0200 |
| Subject | [PATCH 0/8] Add STM32 LPTimer: PWM, trigger and counter |
| Message-ID | <tT1YS-2mF-3@gated-at.bofh.it> |
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
[toc] | [next] | [standalone]
| From | Fabrice Gasnier <fabrice.gasnier@st.com> |
|---|---|
| Date | 2017-06-16 18:10 +0200 |
| Subject | [PATCH 5/8] dt-bindings: iio: Add STM32 LPTimer trigger binding |
| Message-ID | <tT1YS-2mF-21@gated-at.bofh.it> |
| In reply to | #1667856 |
Add documentation for STMicroelectronics STM32 Low Power Timer trigger
binding.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
.../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
[toc] | [prev] | [next] | [standalone]
| From | Fabrice Gasnier <fabrice.gasnier@st.com> |
|---|---|
| Date | 2017-06-16 18:10 +0200 |
| Subject | [PATCH 8/8] iio: counter: Add support for STM32 LPTimer |
| Message-ID | <tT1YT-2mF-27@gated-at.bofh.it> |
| In reply to | #1667856 |
Add support for STM32 Low Power Timer, that can be used as counter
or quadrature encoder.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
.../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: fabrice.gasnier@st.com
+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: fabrice.gasnier@st.com
+Description:
+ Reading returns the list possible quadrature modes.
+
+What: /sys/bus/iio/devices/iio:deviceX/in_count0_quadrature_mode
+KernelVersion: 4.13
+Contact: fabrice.gasnier@st.com
+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: fabrice.gasnier@st.com
+Description:
+ Reading returns the list possible active edges.
+
+What: /sys/bus/iio/devices/iio:deviceX/in_count0_polarity
+KernelVersion: 4.13
+Contact: fabrice.gasnier@st.com
+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 <fabrice.gasnier@st.com>
+ * 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 <fabrice.gasnier@st.com>");
+MODULE_ALIAS("platform:stm32-lptimer-counter");
+MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
+MODULE_LICENSE("GPL v2");
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Fabrice Gasnier <fabrice.gasnier@st.com> |
|---|---|
| Date | 2017-06-16 18:10 +0200 |
| Subject | [PATCH 2/8] mfd: Add STM32 LPTimer driver |
| Message-ID | <tT1YT-2mF-29@gated-at.bofh.it> |
| In reply to | #1667856 |
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 <fabrice.gasnier@st.com>
---
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 <fabrice.gasnier@st.com>
+ * 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 <fabrice.gasnier@st.com>");
+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 <fabrice.gasnier@st.com>
+ * 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
[toc] | [prev] | [next] | [standalone]
| From | Lee Jones <lee.jones@linaro.org> |
|---|---|
| Date | 2017-06-19 18:00 +0200 |
| Subject | Re: [PATCH 2/8] mfd: Add STM32 LPTimer driver |
| Message-ID | <tU7fQ-4Bo-11@gated-at.bofh.it> |
| In reply to | #1667863 |
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 <fabrice.gasnier@st.com>
> ---
> 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 <fabrice.gasnier@st.com>
'\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 <fabrice.gasnier@st.com>");
> +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 <fabrice.gasnier@st.com>
> + * 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
[toc] | [prev] | [next] | [standalone]
| From | Fabrice Gasnier <fabrice.gasnier@st.com> |
|---|---|
| Date | 2017-06-16 18:10 +0200 |
| Subject | [PATCH 3/8] dt-bindings: pwm: Add STM32 LPTimer PWM binding |
| Message-ID | <tT1YT-2mF-33@gated-at.bofh.it> |
| In reply to | #1667856 |
Add documentation for STMicroelectronics STM32 Low Power Timer
PWM binding.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
.../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
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web