All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/9] Add STM32 LPTimer: PWM, trigger and counter
@ 2017-07-07 16:30 ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:30 UTC (permalink / raw)
  To: lee.jones, benjamin.gaignard, jic23, thierry.reding, robh+dt
  Cc: mark.rutland, alexandre.torgue, mcoquelin.stm32, fabrice.gasnier,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm

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

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

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

---
Changes in v3:
- I sent this v3 with various changes, but still need to discuss ABI
  for counter driver (no change on this in v3).
- Remarks for Rob on dt-bindings
- Add validate_device in Trigger driver
- Update PWM driver after Thierry's comments
- Additional patch for STM32 ADC trigger

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

Fabrice Gasnier (9):
  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
  iio: adc: stm32: add support for lptimer triggers

 .../ABI/testing/sysfs-bus-iio-lptimer-stm32        |  57 +++
 .../bindings/iio/counter/stm32-lptimer-cnt.txt     |  27 ++
 .../bindings/iio/timer/stm32-lptimer-trigger.txt   |  23 ++
 .../devicetree/bindings/mfd/stm32-lptimer.txt      |  48 +++
 .../devicetree/bindings/pwm/pwm-stm32-lp.txt       |  24 ++
 drivers/iio/adc/stm32-adc.c                        |  14 +-
 drivers/iio/counter/Kconfig                        |   9 +
 drivers/iio/counter/Makefile                       |   1 +
 drivers/iio/counter/stm32-lptimer-cnt.c            | 383 +++++++++++++++++++++
 drivers/iio/trigger/Kconfig                        |  11 +
 drivers/iio/trigger/Makefile                       |   1 +
 drivers/iio/trigger/stm32-lptimer-trigger.c        | 118 +++++++
 drivers/mfd/Kconfig                                |  14 +
 drivers/mfd/Makefile                               |   1 +
 drivers/mfd/stm32-lptimer.c                        | 107 ++++++
 drivers/pwm/Kconfig                                |  10 +
 drivers/pwm/Makefile                               |   1 +
 drivers/pwm/pwm-stm32-lp.c                         | 246 +++++++++++++
 include/linux/iio/timer/stm32-lptim-trigger.h      |  27 ++
 include/linux/mfd/stm32-lptimer.h                  |  62 ++++
 20 files changed, 1182 insertions(+), 2 deletions(-)
 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

^ permalink raw reply	[flat|nested] 63+ messages in thread

* [PATCH v3 0/9] Add STM32 LPTimer: PWM, trigger and counter
@ 2017-07-07 16:30 ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:30 UTC (permalink / raw)
  To: lee.jones, benjamin.gaignard, jic23, thierry.reding, robh+dt
  Cc: mark.rutland, devicetree, alexandre.torgue, linux-pwm, linux-iio,
	linux-kernel, mcoquelin.stm32, fabrice.gasnier, linux-arm-kernel,
	benjamin.gaignard

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

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

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

---
Changes in v3:
- I sent this v3 with various changes, but still need to discuss ABI
  for counter driver (no change on this in v3).
- Remarks for Rob on dt-bindings
- Add validate_device in Trigger driver
- Update PWM driver after Thierry's comments
- Additional patch for STM32 ADC trigger

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

Fabrice Gasnier (9):
  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
  iio: adc: stm32: add support for lptimer triggers

 .../ABI/testing/sysfs-bus-iio-lptimer-stm32        |  57 +++
 .../bindings/iio/counter/stm32-lptimer-cnt.txt     |  27 ++
 .../bindings/iio/timer/stm32-lptimer-trigger.txt   |  23 ++
 .../devicetree/bindings/mfd/stm32-lptimer.txt      |  48 +++
 .../devicetree/bindings/pwm/pwm-stm32-lp.txt       |  24 ++
 drivers/iio/adc/stm32-adc.c                        |  14 +-
 drivers/iio/counter/Kconfig                        |   9 +
 drivers/iio/counter/Makefile                       |   1 +
 drivers/iio/counter/stm32-lptimer-cnt.c            | 383 +++++++++++++++++++++
 drivers/iio/trigger/Kconfig                        |  11 +
 drivers/iio/trigger/Makefile                       |   1 +
 drivers/iio/trigger/stm32-lptimer-trigger.c        | 118 +++++++
 drivers/mfd/Kconfig                                |  14 +
 drivers/mfd/Makefile                               |   1 +
 drivers/mfd/stm32-lptimer.c                        | 107 ++++++
 drivers/pwm/Kconfig                                |  10 +
 drivers/pwm/Makefile                               |   1 +
 drivers/pwm/pwm-stm32-lp.c                         | 246 +++++++++++++
 include/linux/iio/timer/stm32-lptim-trigger.h      |  27 ++
 include/linux/mfd/stm32-lptimer.h                  |  62 ++++
 20 files changed, 1182 insertions(+), 2 deletions(-)
 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

^ permalink raw reply	[flat|nested] 63+ messages in thread

* [PATCH v3 0/9] Add STM32 LPTimer: PWM, trigger and counter
@ 2017-07-07 16:30 ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:30 UTC (permalink / raw)
  To: linux-arm-kernel

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

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

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

---
Changes in v3:
- I sent this v3 with various changes, but still need to discuss ABI
  for counter driver (no change on this in v3).
- Remarks for Rob on dt-bindings
- Add validate_device in Trigger driver
- Update PWM driver after Thierry's comments
- Additional patch for STM32 ADC trigger

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

Fabrice Gasnier (9):
  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
  iio: adc: stm32: add support for lptimer triggers

 .../ABI/testing/sysfs-bus-iio-lptimer-stm32        |  57 +++
 .../bindings/iio/counter/stm32-lptimer-cnt.txt     |  27 ++
 .../bindings/iio/timer/stm32-lptimer-trigger.txt   |  23 ++
 .../devicetree/bindings/mfd/stm32-lptimer.txt      |  48 +++
 .../devicetree/bindings/pwm/pwm-stm32-lp.txt       |  24 ++
 drivers/iio/adc/stm32-adc.c                        |  14 +-
 drivers/iio/counter/Kconfig                        |   9 +
 drivers/iio/counter/Makefile                       |   1 +
 drivers/iio/counter/stm32-lptimer-cnt.c            | 383 +++++++++++++++++++++
 drivers/iio/trigger/Kconfig                        |  11 +
 drivers/iio/trigger/Makefile                       |   1 +
 drivers/iio/trigger/stm32-lptimer-trigger.c        | 118 +++++++
 drivers/mfd/Kconfig                                |  14 +
 drivers/mfd/Makefile                               |   1 +
 drivers/mfd/stm32-lptimer.c                        | 107 ++++++
 drivers/pwm/Kconfig                                |  10 +
 drivers/pwm/Makefile                               |   1 +
 drivers/pwm/pwm-stm32-lp.c                         | 246 +++++++++++++
 include/linux/iio/timer/stm32-lptim-trigger.h      |  27 ++
 include/linux/mfd/stm32-lptimer.h                  |  62 ++++
 20 files changed, 1182 insertions(+), 2 deletions(-)
 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

^ permalink raw reply	[flat|nested] 63+ messages in thread

* [PATCH v3 1/9] dt-bindings: mfd: Add STM32 LPTimer binding
  2017-07-07 16:30 ` Fabrice Gasnier
  (?)
@ 2017-07-07 16:31   ` Fabrice Gasnier
  -1 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: lee.jones, benjamin.gaignard, jic23, thierry.reding, robh+dt
  Cc: mark.rutland, alexandre.torgue, mcoquelin.stm32, fabrice.gasnier,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm

Add documentation for STMicroelectronics STM32 Low-Power Timer binding.

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Acked-by: Jonathan Cameron <jic23@kernel.org>
---
Changes in v3:
- Rob's comment on node name: timer@...

Changes in v2:
- Lee's comments: s/Low Power/Low-Power/, remove 0x in example, improve
  properties descriptions
---
 .../devicetree/bindings/mfd/stm32-lptimer.txt      | 48 ++++++++++++++++++++++
 1 file changed, 48 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/stm32-lptimer.txt

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

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 1/9] dt-bindings: mfd: Add STM32 LPTimer binding
@ 2017-07-07 16:31   ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: lee.jones, benjamin.gaignard, jic23, thierry.reding, robh+dt
  Cc: mark.rutland, alexandre.torgue, mcoquelin.stm32, fabrice.gasnier,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm

Add documentation for STMicroelectronics STM32 Low-Power Timer binding.

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Acked-by: Jonathan Cameron <jic23@kernel.org>
---
Changes in v3:
- Rob's comment on node name: timer@...

Changes in v2:
- Lee's comments: s/Low Power/Low-Power/, remove 0x in example, improve
  properties descriptions
---
 .../devicetree/bindings/mfd/stm32-lptimer.txt      | 48 ++++++++++++++++++++++
 1 file changed, 48 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/stm32-lptimer.txt

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

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 1/9] dt-bindings: mfd: Add STM32 LPTimer binding
@ 2017-07-07 16:31   ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: linux-arm-kernel

Add documentation for STMicroelectronics STM32 Low-Power Timer binding.

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Acked-by: Jonathan Cameron <jic23@kernel.org>
---
Changes in v3:
- Rob's comment on node name: timer at ...

Changes in v2:
- Lee's comments: s/Low Power/Low-Power/, remove 0x in example, improve
  properties descriptions
---
 .../devicetree/bindings/mfd/stm32-lptimer.txt      | 48 ++++++++++++++++++++++
 1 file changed, 48 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/stm32-lptimer.txt

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

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 2/9] mfd: Add STM32 LPTimer driver
  2017-07-07 16:30 ` Fabrice Gasnier
  (?)
@ 2017-07-07 16:31   ` Fabrice Gasnier
  -1 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: lee.jones, benjamin.gaignard, jic23, thierry.reding, robh+dt
  Cc: mark.rutland, alexandre.torgue, mcoquelin.stm32, fabrice.gasnier,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm

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

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

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 2/9] mfd: Add STM32 LPTimer driver
@ 2017-07-07 16:31   ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: lee.jones, benjamin.gaignard, jic23, thierry.reding, robh+dt
  Cc: mark.rutland, alexandre.torgue, mcoquelin.stm32, fabrice.gasnier,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm

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

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

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 2/9] mfd: Add STM32 LPTimer driver
@ 2017-07-07 16:31   ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: linux-arm-kernel

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

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

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 3/9] dt-bindings: pwm: Add STM32 LPTimer PWM binding
@ 2017-07-07 16:31   ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: lee.jones, benjamin.gaignard, jic23, thierry.reding, robh+dt
  Cc: mark.rutland, alexandre.torgue, mcoquelin.stm32, fabrice.gasnier,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm

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

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Acked-by: Rob Herring <robh@kernel.org>
---
Changes in v3:
- Rob's comment on node name: timer@...

Changes in v2:
- s/Low Power/Low-Power/
- remove 0x in example (parent node)
- improve properties description
---
 .../devicetree/bindings/pwm/pwm-stm32-lp.txt       | 24 ++++++++++++++++++++++
 1 file changed, 24 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pwm/pwm-stm32-lp.txt

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

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 3/9] dt-bindings: pwm: Add STM32 LPTimer PWM binding
@ 2017-07-07 16:31   ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: lee.jones-QSEj5FYQhm4dnm+yROfE0A,
	benjamin.gaignard-QSEj5FYQhm4dnm+yROfE0A,
	jic23-DgEjT+Ai2ygdnm+yROfE0A,
	thierry.reding-Re5JQEeQqe8AvxtiuMwx3w,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A
  Cc: mark.rutland-5wv7dgnIgG8, alexandre.torgue-qxv4g6HH51o,
	mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
	fabrice.gasnier-qxv4g6HH51o, benjamin.gaignard-qxv4g6HH51o,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-pwm-u79uwXL29TY76Z2rM5mHXA

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

Signed-off-by: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
Changes in v3:
- Rob's comment on node name: timer@...

Changes in v2:
- s/Low Power/Low-Power/
- remove 0x in example (parent node)
- improve properties description
---
 .../devicetree/bindings/pwm/pwm-stm32-lp.txt       | 24 ++++++++++++++++++++++
 1 file changed, 24 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pwm/pwm-stm32-lp.txt

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

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 3/9] dt-bindings: pwm: Add STM32 LPTimer PWM binding
@ 2017-07-07 16:31   ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: linux-arm-kernel

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

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Acked-by: Rob Herring <robh@kernel.org>
---
Changes in v3:
- Rob's comment on node name: timer at ...

Changes in v2:
- s/Low Power/Low-Power/
- remove 0x in example (parent node)
- improve properties description
---
 .../devicetree/bindings/pwm/pwm-stm32-lp.txt       | 24 ++++++++++++++++++++++
 1 file changed, 24 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pwm/pwm-stm32-lp.txt

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

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 4/9] pwm: Add STM32 LPTimer PWM driver
@ 2017-07-07 16:31   ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: lee.jones, benjamin.gaignard, jic23, thierry.reding, robh+dt
  Cc: mark.rutland, alexandre.torgue, mcoquelin.stm32, fabrice.gasnier,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm

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

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
Changes in v3:
- remove prescalers[] array, use power-of-2 presc directly
- Update following Thierry's comments:
- fix issue using FIELD_GET() macro
- Add get_state() callback
- remove some checks in probe
- slight rework 'reenable' flag
- use more common method to disable pwm in remove()

Changes in v2:
- s/Low Power/Low-Power
- update few comment lines
---
 drivers/pwm/Kconfig        |  10 ++
 drivers/pwm/Makefile       |   1 +
 drivers/pwm/pwm-stm32-lp.c | 246 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 257 insertions(+)
 create mode 100644 drivers/pwm/pwm-stm32-lp.c

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 313c107..7cb982b 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -417,6 +417,16 @@ config PWM_STM32
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-stm32.
 
+config PWM_STM32_LP
+	tristate "STMicroelectronics STM32 PWM LP"
+	depends on MFD_STM32_LPTIMER || COMPILE_TEST
+	help
+	  Generic PWM framework driver for STMicroelectronics STM32 SoCs
+	  with Low-Power Timer (LPTIM).
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-stm32-lp.
+
 config PWM_STMPE
 	bool "STMPE expander PWM export"
 	depends on MFD_STMPE
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 93da1f7..a3a4bee 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_PWM_SAMSUNG)	+= pwm-samsung.o
 obj-$(CONFIG_PWM_SPEAR)		+= pwm-spear.o
 obj-$(CONFIG_PWM_STI)		+= pwm-sti.o
 obj-$(CONFIG_PWM_STM32)		+= pwm-stm32.o
+obj-$(CONFIG_PWM_STM32_LP)	+= pwm-stm32-lp.o
 obj-$(CONFIG_PWM_STMPE)		+= pwm-stmpe.o
 obj-$(CONFIG_PWM_SUN4I)		+= pwm-sun4i.o
 obj-$(CONFIG_PWM_TEGRA)		+= pwm-tegra.o
diff --git a/drivers/pwm/pwm-stm32-lp.c b/drivers/pwm/pwm-stm32-lp.c
new file mode 100644
index 0000000..9793b29
--- /dev/null
+++ b/drivers/pwm/pwm-stm32-lp.c
@@ -0,0 +1,246 @@
+/*
+ * STM32 Low-Power Timer PWM driver
+ *
+ * Copyright (C) STMicroelectronics 2017
+ *
+ * Author: Gerald Baeza <gerald.baeza@st.com>
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * Inspired by Gerald Baeza's pwm-stm32 driver
+ */
+
+#include <linux/bitfield.h>
+#include <linux/mfd/stm32-lptimer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+struct stm32_pwm_lp {
+	struct pwm_chip chip;
+	struct clk *clk;
+	struct regmap *regmap;
+};
+
+static inline struct stm32_pwm_lp *to_stm32_pwm_lp(struct pwm_chip *chip)
+{
+	return container_of(chip, struct stm32_pwm_lp, chip);
+}
+
+/* STM32 Low-Power Timer is preceded by a configurable power-of-2 prescaler */
+#define STM32_LPTIM_MAX_PRESCALER	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, presc = 0;
+	bool reenable;
+	int ret;
+
+	pwm_get_state(pwm, &cstate);
+	reenable = !cstate.enabled;
+
+	if (!state->enabled) {
+		if (cstate.enabled) {
+			/* Disable LP timer */
+			ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
+			if (ret)
+				return ret;
+			/* disable clock to PWM counter */
+			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 ((1 << presc) > STM32_LPTIM_MAX_PRESCALER) {
+			dev_err(priv->chip.dev, "max prescaler exceeded\n");
+			return -EINVAL;
+		}
+		div = prd >> presc;
+	}
+	prd = div;
+
+	/* Calculate the duty cycle */
+	dty = prd * state->duty_cycle;
+	do_div(dty, state->period);
+
+	if (!cstate.enabled) {
+		/* enable clock to drive PWM counter */
+		ret = clk_enable(priv->clk);
+		if (ret)
+			return ret;
+	}
+
+	ret = regmap_read(priv->regmap, STM32_LPTIM_CFGR, &cfgr);
+	if (ret)
+		goto err;
+
+	if ((FIELD_GET(STM32_LPTIM_PRESC, cfgr) != presc) ||
+	    (FIELD_GET(STM32_LPTIM_WAVPOL, cfgr) != state->polarity)) {
+		val = FIELD_PREP(STM32_LPTIM_PRESC, presc);
+		val |= FIELD_PREP(STM32_LPTIM_WAVPOL, state->polarity);
+		mask = STM32_LPTIM_PRESC | STM32_LPTIM_WAVPOL;
+
+		/* Must disable LP timer to modify CFGR */
+		reenable = true;
+		ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
+		if (ret)
+			goto err;
+
+		ret = regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask,
+					 val);
+		if (ret)
+			goto err;
+	}
+
+	if (reenable) {
+		/* Must (re)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 (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 void stm32_pwm_lp_get_state(struct pwm_chip *chip,
+				   struct pwm_device *pwm,
+				   struct pwm_state *state)
+{
+	struct stm32_pwm_lp *priv = to_stm32_pwm_lp(chip);
+	unsigned long rate = clk_get_rate(priv->clk);
+	u32 val, presc, prd;
+	u64 tmp;
+
+	regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
+	state->enabled = !!FIELD_GET(STM32_LPTIM_ENABLE, val);
+	/* Keep PWM counter clock refcount in sync with PWM initial state */
+	if (state->enabled)
+		clk_enable(priv->clk);
+
+	regmap_read(priv->regmap, STM32_LPTIM_CFGR, &val);
+	presc = FIELD_GET(STM32_LPTIM_PRESC, val);
+	state->polarity = FIELD_GET(STM32_LPTIM_WAVPOL, val);
+
+	regmap_read(priv->regmap, STM32_LPTIM_ARR, &prd);
+	tmp = prd + 1;
+	tmp = (tmp << presc) * NSEC_PER_SEC;
+	state->period = DIV_ROUND_CLOSEST_ULL(tmp, rate);
+
+	regmap_read(priv->regmap, STM32_LPTIM_CMP, &val);
+	tmp = prd - val;
+	tmp = (tmp << presc) * NSEC_PER_SEC;
+	state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, rate);
+}
+
+static const struct pwm_ops stm32_pwm_lp_ops = {
+	.owner = THIS_MODULE,
+	.apply = stm32_pwm_lp_apply,
+	.get_state = stm32_pwm_lp_get_state,
+};
+
+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;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->regmap = ddata->regmap;
+	priv->clk = ddata->clk;
+	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);
+	unsigned int i;
+
+	for (i = 0; i < priv->chip.npwm; i++)
+		if (pwm_is_enabled(&priv->chip.pwms[i]))
+			pwm_disable(&priv->chip.pwms[i]);
+
+	return pwmchip_remove(&priv->chip);
+}
+
+static const struct of_device_id stm32_pwm_lp_of_match[] = {
+	{ .compatible = "st,stm32-pwm-lp", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, stm32_pwm_lp_of_match);
+
+static struct platform_driver stm32_pwm_lp_driver = {
+	.probe	= stm32_pwm_lp_probe,
+	.remove	= stm32_pwm_lp_remove,
+	.driver	= {
+		.name = "stm32-pwm-lp",
+		.of_match_table = of_match_ptr(stm32_pwm_lp_of_match),
+	},
+};
+module_platform_driver(stm32_pwm_lp_driver);
+
+MODULE_ALIAS("platform:stm32-pwm-lp");
+MODULE_DESCRIPTION("STMicroelectronics STM32 PWM LP driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 4/9] pwm: Add STM32 LPTimer PWM driver
@ 2017-07-07 16:31   ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: lee.jones-QSEj5FYQhm4dnm+yROfE0A,
	benjamin.gaignard-QSEj5FYQhm4dnm+yROfE0A,
	jic23-DgEjT+Ai2ygdnm+yROfE0A,
	thierry.reding-Re5JQEeQqe8AvxtiuMwx3w,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A
  Cc: mark.rutland-5wv7dgnIgG8, alexandre.torgue-qxv4g6HH51o,
	mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
	fabrice.gasnier-qxv4g6HH51o, benjamin.gaignard-qxv4g6HH51o,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-pwm-u79uwXL29TY76Z2rM5mHXA

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

Signed-off-by: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
---
Changes in v3:
- remove prescalers[] array, use power-of-2 presc directly
- Update following Thierry's comments:
- fix issue using FIELD_GET() macro
- Add get_state() callback
- remove some checks in probe
- slight rework 'reenable' flag
- use more common method to disable pwm in remove()

Changes in v2:
- s/Low Power/Low-Power
- update few comment lines
---
 drivers/pwm/Kconfig        |  10 ++
 drivers/pwm/Makefile       |   1 +
 drivers/pwm/pwm-stm32-lp.c | 246 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 257 insertions(+)
 create mode 100644 drivers/pwm/pwm-stm32-lp.c

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 313c107..7cb982b 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -417,6 +417,16 @@ config PWM_STM32
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-stm32.
 
+config PWM_STM32_LP
+	tristate "STMicroelectronics STM32 PWM LP"
+	depends on MFD_STM32_LPTIMER || COMPILE_TEST
+	help
+	  Generic PWM framework driver for STMicroelectronics STM32 SoCs
+	  with Low-Power Timer (LPTIM).
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-stm32-lp.
+
 config PWM_STMPE
 	bool "STMPE expander PWM export"
 	depends on MFD_STMPE
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 93da1f7..a3a4bee 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_PWM_SAMSUNG)	+= pwm-samsung.o
 obj-$(CONFIG_PWM_SPEAR)		+= pwm-spear.o
 obj-$(CONFIG_PWM_STI)		+= pwm-sti.o
 obj-$(CONFIG_PWM_STM32)		+= pwm-stm32.o
+obj-$(CONFIG_PWM_STM32_LP)	+= pwm-stm32-lp.o
 obj-$(CONFIG_PWM_STMPE)		+= pwm-stmpe.o
 obj-$(CONFIG_PWM_SUN4I)		+= pwm-sun4i.o
 obj-$(CONFIG_PWM_TEGRA)		+= pwm-tegra.o
diff --git a/drivers/pwm/pwm-stm32-lp.c b/drivers/pwm/pwm-stm32-lp.c
new file mode 100644
index 0000000..9793b29
--- /dev/null
+++ b/drivers/pwm/pwm-stm32-lp.c
@@ -0,0 +1,246 @@
+/*
+ * STM32 Low-Power Timer PWM driver
+ *
+ * Copyright (C) STMicroelectronics 2017
+ *
+ * Author: Gerald Baeza <gerald.baeza-qxv4g6HH51o@public.gmane.org>
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * Inspired by Gerald Baeza's pwm-stm32 driver
+ */
+
+#include <linux/bitfield.h>
+#include <linux/mfd/stm32-lptimer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+struct stm32_pwm_lp {
+	struct pwm_chip chip;
+	struct clk *clk;
+	struct regmap *regmap;
+};
+
+static inline struct stm32_pwm_lp *to_stm32_pwm_lp(struct pwm_chip *chip)
+{
+	return container_of(chip, struct stm32_pwm_lp, chip);
+}
+
+/* STM32 Low-Power Timer is preceded by a configurable power-of-2 prescaler */
+#define STM32_LPTIM_MAX_PRESCALER	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, presc = 0;
+	bool reenable;
+	int ret;
+
+	pwm_get_state(pwm, &cstate);
+	reenable = !cstate.enabled;
+
+	if (!state->enabled) {
+		if (cstate.enabled) {
+			/* Disable LP timer */
+			ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
+			if (ret)
+				return ret;
+			/* disable clock to PWM counter */
+			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 ((1 << presc) > STM32_LPTIM_MAX_PRESCALER) {
+			dev_err(priv->chip.dev, "max prescaler exceeded\n");
+			return -EINVAL;
+		}
+		div = prd >> presc;
+	}
+	prd = div;
+
+	/* Calculate the duty cycle */
+	dty = prd * state->duty_cycle;
+	do_div(dty, state->period);
+
+	if (!cstate.enabled) {
+		/* enable clock to drive PWM counter */
+		ret = clk_enable(priv->clk);
+		if (ret)
+			return ret;
+	}
+
+	ret = regmap_read(priv->regmap, STM32_LPTIM_CFGR, &cfgr);
+	if (ret)
+		goto err;
+
+	if ((FIELD_GET(STM32_LPTIM_PRESC, cfgr) != presc) ||
+	    (FIELD_GET(STM32_LPTIM_WAVPOL, cfgr) != state->polarity)) {
+		val = FIELD_PREP(STM32_LPTIM_PRESC, presc);
+		val |= FIELD_PREP(STM32_LPTIM_WAVPOL, state->polarity);
+		mask = STM32_LPTIM_PRESC | STM32_LPTIM_WAVPOL;
+
+		/* Must disable LP timer to modify CFGR */
+		reenable = true;
+		ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
+		if (ret)
+			goto err;
+
+		ret = regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask,
+					 val);
+		if (ret)
+			goto err;
+	}
+
+	if (reenable) {
+		/* Must (re)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 (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 void stm32_pwm_lp_get_state(struct pwm_chip *chip,
+				   struct pwm_device *pwm,
+				   struct pwm_state *state)
+{
+	struct stm32_pwm_lp *priv = to_stm32_pwm_lp(chip);
+	unsigned long rate = clk_get_rate(priv->clk);
+	u32 val, presc, prd;
+	u64 tmp;
+
+	regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
+	state->enabled = !!FIELD_GET(STM32_LPTIM_ENABLE, val);
+	/* Keep PWM counter clock refcount in sync with PWM initial state */
+	if (state->enabled)
+		clk_enable(priv->clk);
+
+	regmap_read(priv->regmap, STM32_LPTIM_CFGR, &val);
+	presc = FIELD_GET(STM32_LPTIM_PRESC, val);
+	state->polarity = FIELD_GET(STM32_LPTIM_WAVPOL, val);
+
+	regmap_read(priv->regmap, STM32_LPTIM_ARR, &prd);
+	tmp = prd + 1;
+	tmp = (tmp << presc) * NSEC_PER_SEC;
+	state->period = DIV_ROUND_CLOSEST_ULL(tmp, rate);
+
+	regmap_read(priv->regmap, STM32_LPTIM_CMP, &val);
+	tmp = prd - val;
+	tmp = (tmp << presc) * NSEC_PER_SEC;
+	state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, rate);
+}
+
+static const struct pwm_ops stm32_pwm_lp_ops = {
+	.owner = THIS_MODULE,
+	.apply = stm32_pwm_lp_apply,
+	.get_state = stm32_pwm_lp_get_state,
+};
+
+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;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->regmap = ddata->regmap;
+	priv->clk = ddata->clk;
+	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);
+	unsigned int i;
+
+	for (i = 0; i < priv->chip.npwm; i++)
+		if (pwm_is_enabled(&priv->chip.pwms[i]))
+			pwm_disable(&priv->chip.pwms[i]);
+
+	return pwmchip_remove(&priv->chip);
+}
+
+static const struct of_device_id stm32_pwm_lp_of_match[] = {
+	{ .compatible = "st,stm32-pwm-lp", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, stm32_pwm_lp_of_match);
+
+static struct platform_driver stm32_pwm_lp_driver = {
+	.probe	= stm32_pwm_lp_probe,
+	.remove	= stm32_pwm_lp_remove,
+	.driver	= {
+		.name = "stm32-pwm-lp",
+		.of_match_table = of_match_ptr(stm32_pwm_lp_of_match),
+	},
+};
+module_platform_driver(stm32_pwm_lp_driver);
+
+MODULE_ALIAS("platform:stm32-pwm-lp");
+MODULE_DESCRIPTION("STMicroelectronics STM32 PWM LP driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 4/9] pwm: Add STM32 LPTimer PWM driver
@ 2017-07-07 16:31   ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: linux-arm-kernel

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

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
Changes in v3:
- remove prescalers[] array, use power-of-2 presc directly
- Update following Thierry's comments:
- fix issue using FIELD_GET() macro
- Add get_state() callback
- remove some checks in probe
- slight rework 'reenable' flag
- use more common method to disable pwm in remove()

Changes in v2:
- s/Low Power/Low-Power
- update few comment lines
---
 drivers/pwm/Kconfig        |  10 ++
 drivers/pwm/Makefile       |   1 +
 drivers/pwm/pwm-stm32-lp.c | 246 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 257 insertions(+)
 create mode 100644 drivers/pwm/pwm-stm32-lp.c

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 313c107..7cb982b 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -417,6 +417,16 @@ config PWM_STM32
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-stm32.
 
+config PWM_STM32_LP
+	tristate "STMicroelectronics STM32 PWM LP"
+	depends on MFD_STM32_LPTIMER || COMPILE_TEST
+	help
+	  Generic PWM framework driver for STMicroelectronics STM32 SoCs
+	  with Low-Power Timer (LPTIM).
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-stm32-lp.
+
 config PWM_STMPE
 	bool "STMPE expander PWM export"
 	depends on MFD_STMPE
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 93da1f7..a3a4bee 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_PWM_SAMSUNG)	+= pwm-samsung.o
 obj-$(CONFIG_PWM_SPEAR)		+= pwm-spear.o
 obj-$(CONFIG_PWM_STI)		+= pwm-sti.o
 obj-$(CONFIG_PWM_STM32)		+= pwm-stm32.o
+obj-$(CONFIG_PWM_STM32_LP)	+= pwm-stm32-lp.o
 obj-$(CONFIG_PWM_STMPE)		+= pwm-stmpe.o
 obj-$(CONFIG_PWM_SUN4I)		+= pwm-sun4i.o
 obj-$(CONFIG_PWM_TEGRA)		+= pwm-tegra.o
diff --git a/drivers/pwm/pwm-stm32-lp.c b/drivers/pwm/pwm-stm32-lp.c
new file mode 100644
index 0000000..9793b29
--- /dev/null
+++ b/drivers/pwm/pwm-stm32-lp.c
@@ -0,0 +1,246 @@
+/*
+ * STM32 Low-Power Timer PWM driver
+ *
+ * Copyright (C) STMicroelectronics 2017
+ *
+ * Author: Gerald Baeza <gerald.baeza@st.com>
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * Inspired by Gerald Baeza's pwm-stm32 driver
+ */
+
+#include <linux/bitfield.h>
+#include <linux/mfd/stm32-lptimer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+struct stm32_pwm_lp {
+	struct pwm_chip chip;
+	struct clk *clk;
+	struct regmap *regmap;
+};
+
+static inline struct stm32_pwm_lp *to_stm32_pwm_lp(struct pwm_chip *chip)
+{
+	return container_of(chip, struct stm32_pwm_lp, chip);
+}
+
+/* STM32 Low-Power Timer is preceded by a configurable power-of-2 prescaler */
+#define STM32_LPTIM_MAX_PRESCALER	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, presc = 0;
+	bool reenable;
+	int ret;
+
+	pwm_get_state(pwm, &cstate);
+	reenable = !cstate.enabled;
+
+	if (!state->enabled) {
+		if (cstate.enabled) {
+			/* Disable LP timer */
+			ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
+			if (ret)
+				return ret;
+			/* disable clock to PWM counter */
+			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 ((1 << presc) > STM32_LPTIM_MAX_PRESCALER) {
+			dev_err(priv->chip.dev, "max prescaler exceeded\n");
+			return -EINVAL;
+		}
+		div = prd >> presc;
+	}
+	prd = div;
+
+	/* Calculate the duty cycle */
+	dty = prd * state->duty_cycle;
+	do_div(dty, state->period);
+
+	if (!cstate.enabled) {
+		/* enable clock to drive PWM counter */
+		ret = clk_enable(priv->clk);
+		if (ret)
+			return ret;
+	}
+
+	ret = regmap_read(priv->regmap, STM32_LPTIM_CFGR, &cfgr);
+	if (ret)
+		goto err;
+
+	if ((FIELD_GET(STM32_LPTIM_PRESC, cfgr) != presc) ||
+	    (FIELD_GET(STM32_LPTIM_WAVPOL, cfgr) != state->polarity)) {
+		val = FIELD_PREP(STM32_LPTIM_PRESC, presc);
+		val |= FIELD_PREP(STM32_LPTIM_WAVPOL, state->polarity);
+		mask = STM32_LPTIM_PRESC | STM32_LPTIM_WAVPOL;
+
+		/* Must disable LP timer to modify CFGR */
+		reenable = true;
+		ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
+		if (ret)
+			goto err;
+
+		ret = regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask,
+					 val);
+		if (ret)
+			goto err;
+	}
+
+	if (reenable) {
+		/* Must (re)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 (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 void stm32_pwm_lp_get_state(struct pwm_chip *chip,
+				   struct pwm_device *pwm,
+				   struct pwm_state *state)
+{
+	struct stm32_pwm_lp *priv = to_stm32_pwm_lp(chip);
+	unsigned long rate = clk_get_rate(priv->clk);
+	u32 val, presc, prd;
+	u64 tmp;
+
+	regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
+	state->enabled = !!FIELD_GET(STM32_LPTIM_ENABLE, val);
+	/* Keep PWM counter clock refcount in sync with PWM initial state */
+	if (state->enabled)
+		clk_enable(priv->clk);
+
+	regmap_read(priv->regmap, STM32_LPTIM_CFGR, &val);
+	presc = FIELD_GET(STM32_LPTIM_PRESC, val);
+	state->polarity = FIELD_GET(STM32_LPTIM_WAVPOL, val);
+
+	regmap_read(priv->regmap, STM32_LPTIM_ARR, &prd);
+	tmp = prd + 1;
+	tmp = (tmp << presc) * NSEC_PER_SEC;
+	state->period = DIV_ROUND_CLOSEST_ULL(tmp, rate);
+
+	regmap_read(priv->regmap, STM32_LPTIM_CMP, &val);
+	tmp = prd - val;
+	tmp = (tmp << presc) * NSEC_PER_SEC;
+	state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, rate);
+}
+
+static const struct pwm_ops stm32_pwm_lp_ops = {
+	.owner = THIS_MODULE,
+	.apply = stm32_pwm_lp_apply,
+	.get_state = stm32_pwm_lp_get_state,
+};
+
+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;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->regmap = ddata->regmap;
+	priv->clk = ddata->clk;
+	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);
+	unsigned int i;
+
+	for (i = 0; i < priv->chip.npwm; i++)
+		if (pwm_is_enabled(&priv->chip.pwms[i]))
+			pwm_disable(&priv->chip.pwms[i]);
+
+	return pwmchip_remove(&priv->chip);
+}
+
+static const struct of_device_id stm32_pwm_lp_of_match[] = {
+	{ .compatible = "st,stm32-pwm-lp", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, stm32_pwm_lp_of_match);
+
+static struct platform_driver stm32_pwm_lp_driver = {
+	.probe	= stm32_pwm_lp_probe,
+	.remove	= stm32_pwm_lp_remove,
+	.driver	= {
+		.name = "stm32-pwm-lp",
+		.of_match_table = of_match_ptr(stm32_pwm_lp_of_match),
+	},
+};
+module_platform_driver(stm32_pwm_lp_driver);
+
+MODULE_ALIAS("platform:stm32-pwm-lp");
+MODULE_DESCRIPTION("STMicroelectronics STM32 PWM LP driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 5/9] dt-bindings: iio: Add STM32 LPTimer trigger binding
  2017-07-07 16:30 ` Fabrice Gasnier
  (?)
@ 2017-07-07 16:31   ` Fabrice Gasnier
  -1 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: lee.jones, benjamin.gaignard, jic23, thierry.reding, robh+dt
  Cc: mark.rutland, alexandre.torgue, mcoquelin.stm32, fabrice.gasnier,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm

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

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Acked-by: Jonathan Cameron <jic23@kernel.org>
---
Changes in v3:
- Rob's comments: node name timer@..., update reg property
  description

Changes in v2:
- s/Low Power/Low-Power
- remove leading 0x in example (parent node)
- improve reg property description
---
 .../bindings/iio/timer/stm32-lptimer-trigger.txt   | 23 ++++++++++++++++++++++
 1 file changed, 23 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt

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

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 5/9] dt-bindings: iio: Add STM32 LPTimer trigger binding
@ 2017-07-07 16:31   ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: lee.jones, benjamin.gaignard, jic23, thierry.reding, robh+dt
  Cc: mark.rutland, devicetree, alexandre.torgue, linux-pwm, linux-iio,
	linux-kernel, mcoquelin.stm32, fabrice.gasnier, linux-arm-kernel,
	benjamin.gaignard

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

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Acked-by: Jonathan Cameron <jic23@kernel.org>
---
Changes in v3:
- Rob's comments: node name timer@..., update reg property
  description

Changes in v2:
- s/Low Power/Low-Power
- remove leading 0x in example (parent node)
- improve reg property description
---
 .../bindings/iio/timer/stm32-lptimer-trigger.txt   | 23 ++++++++++++++++++++++
 1 file changed, 23 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt

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

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 5/9] dt-bindings: iio: Add STM32 LPTimer trigger binding
@ 2017-07-07 16:31   ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: linux-arm-kernel

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

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Acked-by: Jonathan Cameron <jic23@kernel.org>
---
Changes in v3:
- Rob's comments: node name timer at ..., update reg property
  description

Changes in v2:
- s/Low Power/Low-Power
- remove leading 0x in example (parent node)
- improve reg property description
---
 .../bindings/iio/timer/stm32-lptimer-trigger.txt   | 23 ++++++++++++++++++++++
 1 file changed, 23 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt

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

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 6/9] iio: trigger: Add STM32 LPTimer trigger driver
  2017-07-07 16:30 ` Fabrice Gasnier
  (?)
@ 2017-07-07 16:31   ` Fabrice Gasnier
  -1 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: lee.jones, benjamin.gaignard, jic23, thierry.reding, robh+dt
  Cc: mark.rutland, alexandre.torgue, mcoquelin.stm32, fabrice.gasnier,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm

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 <fabrice.gasnier@st.com>
---
Changes in v3:
- Add validate_device callback

Changes in v2:
- s/Low Power/Low-Power
- update few comments
---
 drivers/iio/trigger/Kconfig                   |  11 +++
 drivers/iio/trigger/Makefile                  |   1 +
 drivers/iio/trigger/stm32-lptimer-trigger.c   | 118 ++++++++++++++++++++++++++
 include/linux/iio/timer/stm32-lptim-trigger.h |  27 ++++++
 4 files changed, 157 insertions(+)
 create mode 100644 drivers/iio/trigger/stm32-lptimer-trigger.c
 create mode 100644 include/linux/iio/timer/stm32-lptim-trigger.h

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

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 6/9] iio: trigger: Add STM32 LPTimer trigger driver
@ 2017-07-07 16:31   ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: lee.jones, benjamin.gaignard, jic23, thierry.reding, robh+dt
  Cc: mark.rutland, alexandre.torgue, mcoquelin.stm32, fabrice.gasnier,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm

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 <fabrice.gasnier@st.com>
---
Changes in v3:
- Add validate_device callback

Changes in v2:
- s/Low Power/Low-Power
- update few comments
---
 drivers/iio/trigger/Kconfig                   |  11 +++
 drivers/iio/trigger/Makefile                  |   1 +
 drivers/iio/trigger/stm32-lptimer-trigger.c   | 118 ++++++++++++++++++++++++++
 include/linux/iio/timer/stm32-lptim-trigger.h |  27 ++++++
 4 files changed, 157 insertions(+)
 create mode 100644 drivers/iio/trigger/stm32-lptimer-trigger.c
 create mode 100644 include/linux/iio/timer/stm32-lptim-trigger.h

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

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 6/9] iio: trigger: Add STM32 LPTimer trigger driver
@ 2017-07-07 16:31   ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: linux-arm-kernel

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 <fabrice.gasnier@st.com>
---
Changes in v3:
- Add validate_device callback

Changes in v2:
- s/Low Power/Low-Power
- update few comments
---
 drivers/iio/trigger/Kconfig                   |  11 +++
 drivers/iio/trigger/Makefile                  |   1 +
 drivers/iio/trigger/stm32-lptimer-trigger.c   | 118 ++++++++++++++++++++++++++
 include/linux/iio/timer/stm32-lptim-trigger.h |  27 ++++++
 4 files changed, 157 insertions(+)
 create mode 100644 drivers/iio/trigger/stm32-lptimer-trigger.c
 create mode 100644 include/linux/iio/timer/stm32-lptim-trigger.h

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

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 7/9] dt-bindings: iio: Add STM32 LPTimer quadrature encoder and counter
  2017-07-07 16:30 ` Fabrice Gasnier
  (?)
@ 2017-07-07 16:31   ` Fabrice Gasnier
  -1 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: lee.jones, benjamin.gaignard, jic23, thierry.reding, robh+dt
  Cc: mark.rutland, alexandre.torgue, mcoquelin.stm32, fabrice.gasnier,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm

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

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Acked-by: Jonathan Cameron <jic23@kernel.org>
Acked-by: Rob Herring <robh@kernel.org>
---
Changes in v3:
- Rob's comment on node name: timer@...

Changes in v2:
- s/Low Power/Low-Power
- remove leading 0x in example (parent node)
- improve properties descriptions
---
 .../bindings/iio/counter/stm32-lptimer-cnt.txt     | 27 ++++++++++++++++++++++
 1 file changed, 27 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/counter/stm32-lptimer-cnt.txt

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

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 7/9] dt-bindings: iio: Add STM32 LPTimer quadrature encoder and counter
@ 2017-07-07 16:31   ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: lee.jones, benjamin.gaignard, jic23, thierry.reding, robh+dt
  Cc: mark.rutland, alexandre.torgue, mcoquelin.stm32, fabrice.gasnier,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm

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

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Acked-by: Jonathan Cameron <jic23@kernel.org>
Acked-by: Rob Herring <robh@kernel.org>
---
Changes in v3:
- Rob's comment on node name: timer@...

Changes in v2:
- s/Low Power/Low-Power
- remove leading 0x in example (parent node)
- improve properties descriptions
---
 .../bindings/iio/counter/stm32-lptimer-cnt.txt     | 27 ++++++++++++++++++++++
 1 file changed, 27 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/counter/stm32-lptimer-cnt.txt

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

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 7/9] dt-bindings: iio: Add STM32 LPTimer quadrature encoder and counter
@ 2017-07-07 16:31   ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: linux-arm-kernel

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

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Acked-by: Jonathan Cameron <jic23@kernel.org>
Acked-by: Rob Herring <robh@kernel.org>
---
Changes in v3:
- Rob's comment on node name: timer at ...

Changes in v2:
- s/Low Power/Low-Power
- remove leading 0x in example (parent node)
- improve properties descriptions
---
 .../bindings/iio/counter/stm32-lptimer-cnt.txt     | 27 ++++++++++++++++++++++
 1 file changed, 27 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/counter/stm32-lptimer-cnt.txt

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

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 8/9] iio: counter: Add support for STM32 LPTimer
  2017-07-07 16:30 ` Fabrice Gasnier
  (?)
@ 2017-07-07 16:31   ` Fabrice Gasnier
  -1 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: lee.jones, benjamin.gaignard, jic23, thierry.reding, robh+dt
  Cc: mark.rutland, alexandre.torgue, mcoquelin.stm32, fabrice.gasnier,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm

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>
---
Changes in v2:
- s/Low Power/Low-Power
- update few comments
---
 .../ABI/testing/sysfs-bus-iio-lptimer-stm32        |  57 +++
 drivers/iio/counter/Kconfig                        |   9 +
 drivers/iio/counter/Makefile                       |   1 +
 drivers/iio/counter/stm32-lptimer-cnt.c            | 383 +++++++++++++++++++++
 4 files changed, 450 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
 create mode 100644 drivers/iio/counter/stm32-lptimer-cnt.c

diff --git a/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32 b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
new file mode 100644
index 0000000..ad2cc63
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
@@ -0,0 +1,57 @@
+What:		/sys/bus/iio/devices/iio:deviceX/in_count0_preset
+KernelVersion:	4.13
+Contact:	fabrice.gasnier@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..474e1ac 100644
--- a/drivers/iio/counter/Kconfig
+++ b/drivers/iio/counter/Kconfig
@@ -21,4 +21,13 @@ config 104_QUAD_8
 	  The base port addresses for the devices may be configured via the base
 	  array module parameter.
 
+config STM32_LPTIMER_CNT
+	tristate "STM32 LP Timer encoder counter driver"
+	depends on MFD_STM32_LPTIMER || COMPILE_TEST
+	help
+	  Select this option to enable STM32 Low-Power Timer quadrature encoder
+	  and counter driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called stm32-lptimer-cnt.
 endmenu
diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
index 007e884..1b9a896 100644
--- a/drivers/iio/counter/Makefile
+++ b/drivers/iio/counter/Makefile
@@ -5,3 +5,4 @@
 # When adding new entries keep the list in alphabetical order
 
 obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
+obj-$(CONFIG_STM32_LPTIMER_CNT)	+= stm32-lptimer-cnt.o
diff --git a/drivers/iio/counter/stm32-lptimer-cnt.c b/drivers/iio/counter/stm32-lptimer-cnt.c
new file mode 100644
index 0000000..1c5909b
--- /dev/null
+++ b/drivers/iio/counter/stm32-lptimer-cnt.c
@@ -0,0 +1,383 @@
+/*
+ * STM32 Low-Power Timer Encoder and Counter driver
+ *
+ * Copyright (C) STMicroelectronics 2017
+ *
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>
+ *
+ * Inspired by 104-quad-8 and stm32-timer-trigger drivers.
+ *
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/bitfield.h>
+#include <linux/iio/iio.h>
+#include <linux/mfd/stm32-lptimer.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+struct stm32_lptim_cnt {
+	struct device *dev;
+	struct regmap *regmap;
+	struct clk *clk;
+	u32 preset;
+	u32 polarity;
+	u32 quadrature_mode;
+};
+
+static int stm32_lptim_is_enabled(struct stm32_lptim_cnt *priv)
+{
+	u32 val;
+	int ret;
+
+	ret = regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
+	if (ret)
+		return ret;
+
+	return FIELD_GET(STM32_LPTIM_ENABLE, val);
+}
+
+static int stm32_lptim_set_enable_state(struct stm32_lptim_cnt *priv,
+					int enable)
+{
+	int ret;
+	u32 val;
+
+	val = FIELD_PREP(STM32_LPTIM_ENABLE, enable);
+	ret = regmap_write(priv->regmap, STM32_LPTIM_CR, val);
+	if (ret)
+		return ret;
+
+	if (!enable) {
+		clk_disable(priv->clk);
+		return 0;
+	}
+
+	/* LP timer must be enabled before writing CMP & ARR */
+	ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, priv->preset);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, 0);
+	if (ret)
+		return ret;
+
+	/* ensure CMP & ARR registers are properly written */
+	ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val,
+				       (val & STM32_LPTIM_CMPOK_ARROK),
+				       100, 1000);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,
+			   STM32_LPTIM_CMPOKCF_ARROKCF);
+	if (ret)
+		return ret;
+
+	ret = clk_enable(priv->clk);
+	if (ret) {
+		regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
+		return ret;
+	}
+
+	/* Start LP timer in continuous mode */
+	return regmap_update_bits(priv->regmap, STM32_LPTIM_CR,
+				  STM32_LPTIM_CNTSTRT, STM32_LPTIM_CNTSTRT);
+}
+
+static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
+{
+	u32 mask = STM32_LPTIM_ENC | STM32_LPTIM_COUNTMODE |
+		   STM32_LPTIM_CKPOL | STM32_LPTIM_PRESC;
+	u32 val;
+
+	/* Setup LP timer encoder/counter and polarity, without prescaler */
+	if (priv->quadrature_mode)
+		val = enable ? STM32_LPTIM_ENC : 0;
+	else
+		val = enable ? STM32_LPTIM_COUNTMODE : 0;
+	val |= FIELD_PREP(STM32_LPTIM_CKPOL, enable ? priv->polarity : 0);
+
+	return regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask, val);
+}
+
+static int stm32_lptim_write_raw(struct iio_dev *indio_dev,
+				 struct iio_chan_spec const *chan,
+				 int val, int val2, long mask)
+{
+	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_ENABLE:
+		if (val < 0 || val > 1)
+			return -EINVAL;
+
+		/* Check nobody uses the timer, or already disabled/enabled */
+		ret = stm32_lptim_is_enabled(priv);
+		if ((ret < 0) || (!ret && !val))
+			return ret;
+		if (val && ret)
+			return -EBUSY;
+
+		ret = stm32_lptim_setup(priv, val);
+		if (ret)
+			return ret;
+		return stm32_lptim_set_enable_state(priv, val);
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int stm32_lptim_read_raw(struct iio_dev *indio_dev,
+				struct iio_chan_spec const *chan,
+				int *val, int *val2, long mask)
+{
+	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
+	u32 dat;
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = regmap_read(priv->regmap, STM32_LPTIM_CNT, &dat);
+		if (ret)
+			return ret;
+		*val = dat;
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_ENABLE:
+		ret = stm32_lptim_is_enabled(priv);
+		if (ret < 0)
+			return ret;
+		*val = ret;
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_SCALE:
+		/* Non-quadrature mode: scale = 1 */
+		*val = 1;
+		*val2 = 0;
+		if (priv->quadrature_mode) {
+			/*
+			 * Quadrature encoder mode:
+			 * - both edges, quarter cycle, scale is 0.25
+			 * - either rising/falling edge scale is 0.5
+			 */
+			if (priv->polarity > 1)
+				*val2 = 2;
+			else
+				*val2 = 1;
+		}
+		return IIO_VAL_FRACTIONAL_LOG2;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info stm32_lptim_cnt_iio_info = {
+	.read_raw = stm32_lptim_read_raw,
+	.write_raw = stm32_lptim_write_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static const char *const stm32_lptim_quadrature_modes[] = {
+	"non-quadrature",
+	"quadrature",
+};
+
+static int stm32_lptim_get_quadrature_mode(struct iio_dev *indio_dev,
+					   const struct iio_chan_spec *chan)
+{
+	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
+
+	return priv->quadrature_mode;
+}
+
+static int stm32_lptim_set_quadrature_mode(struct iio_dev *indio_dev,
+					   const struct iio_chan_spec *chan,
+					   unsigned int type)
+{
+	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
+
+	if (stm32_lptim_is_enabled(priv))
+		return -EBUSY;
+
+	priv->quadrature_mode = type;
+
+	return 0;
+}
+
+static const struct iio_enum stm32_lptim_quadrature_mode_en = {
+	.items = stm32_lptim_quadrature_modes,
+	.num_items = ARRAY_SIZE(stm32_lptim_quadrature_modes),
+	.get = stm32_lptim_get_quadrature_mode,
+	.set = stm32_lptim_set_quadrature_mode,
+};
+
+static const char * const stm32_lptim_cnt_polarity[] = {
+	"rising-edge", "falling-edge", "both-edges",
+};
+
+static int stm32_lptim_cnt_get_polarity(struct iio_dev *indio_dev,
+					const struct iio_chan_spec *chan)
+{
+	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
+
+	return priv->polarity;
+}
+
+static int stm32_lptim_cnt_set_polarity(struct iio_dev *indio_dev,
+					const struct iio_chan_spec *chan,
+					unsigned int type)
+{
+	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
+
+	if (stm32_lptim_is_enabled(priv))
+		return -EBUSY;
+
+	priv->polarity = type;
+
+	return 0;
+}
+
+static const struct iio_enum stm32_lptim_cnt_polarity_en = {
+	.items = stm32_lptim_cnt_polarity,
+	.num_items = ARRAY_SIZE(stm32_lptim_cnt_polarity),
+	.get = stm32_lptim_cnt_get_polarity,
+	.set = stm32_lptim_cnt_set_polarity,
+};
+
+static ssize_t stm32_lptim_cnt_get_preset(struct iio_dev *indio_dev,
+					  uintptr_t private,
+					  const struct iio_chan_spec *chan,
+					  char *buf)
+{
+	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset);
+}
+
+static ssize_t stm32_lptim_cnt_set_preset(struct iio_dev *indio_dev,
+					  uintptr_t private,
+					  const struct iio_chan_spec *chan,
+					  const char *buf, size_t len)
+{
+	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
+	int ret;
+
+	if (stm32_lptim_is_enabled(priv))
+		return -EBUSY;
+
+	ret = kstrtouint(buf, 0, &priv->preset);
+	if (ret)
+		return ret;
+
+	if (priv->preset > STM32_LPTIM_MAX_ARR)
+		return -EINVAL;
+
+	return len;
+}
+
+/* LP timer with encoder */
+static const struct iio_chan_spec_ext_info stm32_lptim_enc_ext_info[] = {
+	{
+		.name = "preset",
+		.shared = IIO_SEPARATE,
+		.read = stm32_lptim_cnt_get_preset,
+		.write = stm32_lptim_cnt_set_preset,
+	},
+	IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
+	IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
+	IIO_ENUM("quadrature_mode", IIO_SEPARATE,
+		 &stm32_lptim_quadrature_mode_en),
+	IIO_ENUM_AVAILABLE("quadrature_mode", &stm32_lptim_quadrature_mode_en),
+	{}
+};
+
+static const struct iio_chan_spec stm32_lptim_enc_channels = {
+	.type = IIO_COUNT,
+	.channel = 0,
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+			      BIT(IIO_CHAN_INFO_ENABLE) |
+			      BIT(IIO_CHAN_INFO_SCALE),
+	.ext_info = stm32_lptim_enc_ext_info,
+	.indexed = 1,
+};
+
+/* LP timer without encoder (counter only) */
+static const struct iio_chan_spec_ext_info stm32_lptim_cnt_ext_info[] = {
+	{
+		.name = "preset",
+		.shared = IIO_SEPARATE,
+		.read = stm32_lptim_cnt_get_preset,
+		.write = stm32_lptim_cnt_set_preset,
+	},
+	IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
+	IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
+	{}
+};
+
+static const struct iio_chan_spec stm32_lptim_cnt_channels = {
+	.type = IIO_COUNT,
+	.channel = 0,
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+			      BIT(IIO_CHAN_INFO_ENABLE) |
+			      BIT(IIO_CHAN_INFO_SCALE),
+	.ext_info = stm32_lptim_cnt_ext_info,
+	.indexed = 1,
+};
+
+static int stm32_lptim_cnt_probe(struct platform_device *pdev)
+{
+	struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
+	struct stm32_lptim_cnt *priv;
+	struct iio_dev *indio_dev;
+
+	if (IS_ERR_OR_NULL(ddata))
+		return -EINVAL;
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	priv = iio_priv(indio_dev);
+	priv->dev = &pdev->dev;
+	priv->regmap = ddata->regmap;
+	priv->clk = ddata->clk;
+	priv->preset = STM32_LPTIM_MAX_ARR;
+
+	indio_dev->name = dev_name(&pdev->dev);
+	indio_dev->dev.parent = &pdev->dev;
+	indio_dev->dev.of_node = pdev->dev.of_node;
+	indio_dev->info = &stm32_lptim_cnt_iio_info;
+	if (ddata->has_encoder)
+		indio_dev->channels = &stm32_lptim_enc_channels;
+	else
+		indio_dev->channels = &stm32_lptim_cnt_channels;
+	indio_dev->num_channels = 1;
+
+	platform_set_drvdata(pdev, priv);
+
+	return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+
+static const struct of_device_id stm32_lptim_cnt_of_match[] = {
+	{ .compatible = "st,stm32-lptimer-counter", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, stm32_lptim_cnt_of_match);
+
+static struct platform_driver stm32_lptim_cnt_driver = {
+	.probe = stm32_lptim_cnt_probe,
+	.driver = {
+		.name = "stm32-lptimer-counter",
+		.of_match_table = stm32_lptim_cnt_of_match,
+	},
+};
+module_platform_driver(stm32_lptim_cnt_driver);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_ALIAS("platform:stm32-lptimer-counter");
+MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 8/9] iio: counter: Add support for STM32 LPTimer
@ 2017-07-07 16:31   ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: lee.jones, benjamin.gaignard, jic23, thierry.reding, robh+dt
  Cc: mark.rutland, alexandre.torgue, mcoquelin.stm32, fabrice.gasnier,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm

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>
---
Changes in v2:
- s/Low Power/Low-Power
- update few comments
---
 .../ABI/testing/sysfs-bus-iio-lptimer-stm32        |  57 +++
 drivers/iio/counter/Kconfig                        |   9 +
 drivers/iio/counter/Makefile                       |   1 +
 drivers/iio/counter/stm32-lptimer-cnt.c            | 383 +++++++++++++++++++++
 4 files changed, 450 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
 create mode 100644 drivers/iio/counter/stm32-lptimer-cnt.c

diff --git a/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32 b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
new file mode 100644
index 0000000..ad2cc63
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
@@ -0,0 +1,57 @@
+What:		/sys/bus/iio/devices/iio:deviceX/in_count0_preset
+KernelVersion:	4.13
+Contact:	fabrice.gasnier@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..474e1ac 100644
--- a/drivers/iio/counter/Kconfig
+++ b/drivers/iio/counter/Kconfig
@@ -21,4 +21,13 @@ config 104_QUAD_8
 	  The base port addresses for the devices may be configured via the base
 	  array module parameter.
 
+config STM32_LPTIMER_CNT
+	tristate "STM32 LP Timer encoder counter driver"
+	depends on MFD_STM32_LPTIMER || COMPILE_TEST
+	help
+	  Select this option to enable STM32 Low-Power Timer quadrature encoder
+	  and counter driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called stm32-lptimer-cnt.
 endmenu
diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
index 007e884..1b9a896 100644
--- a/drivers/iio/counter/Makefile
+++ b/drivers/iio/counter/Makefile
@@ -5,3 +5,4 @@
 # When adding new entries keep the list in alphabetical order
 
 obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
+obj-$(CONFIG_STM32_LPTIMER_CNT)	+= stm32-lptimer-cnt.o
diff --git a/drivers/iio/counter/stm32-lptimer-cnt.c b/drivers/iio/counter/stm32-lptimer-cnt.c
new file mode 100644
index 0000000..1c5909b
--- /dev/null
+++ b/drivers/iio/counter/stm32-lptimer-cnt.c
@@ -0,0 +1,383 @@
+/*
+ * STM32 Low-Power Timer Encoder and Counter driver
+ *
+ * Copyright (C) STMicroelectronics 2017
+ *
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>
+ *
+ * Inspired by 104-quad-8 and stm32-timer-trigger drivers.
+ *
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/bitfield.h>
+#include <linux/iio/iio.h>
+#include <linux/mfd/stm32-lptimer.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+struct stm32_lptim_cnt {
+	struct device *dev;
+	struct regmap *regmap;
+	struct clk *clk;
+	u32 preset;
+	u32 polarity;
+	u32 quadrature_mode;
+};
+
+static int stm32_lptim_is_enabled(struct stm32_lptim_cnt *priv)
+{
+	u32 val;
+	int ret;
+
+	ret = regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
+	if (ret)
+		return ret;
+
+	return FIELD_GET(STM32_LPTIM_ENABLE, val);
+}
+
+static int stm32_lptim_set_enable_state(struct stm32_lptim_cnt *priv,
+					int enable)
+{
+	int ret;
+	u32 val;
+
+	val = FIELD_PREP(STM32_LPTIM_ENABLE, enable);
+	ret = regmap_write(priv->regmap, STM32_LPTIM_CR, val);
+	if (ret)
+		return ret;
+
+	if (!enable) {
+		clk_disable(priv->clk);
+		return 0;
+	}
+
+	/* LP timer must be enabled before writing CMP & ARR */
+	ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, priv->preset);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, 0);
+	if (ret)
+		return ret;
+
+	/* ensure CMP & ARR registers are properly written */
+	ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val,
+				       (val & STM32_LPTIM_CMPOK_ARROK),
+				       100, 1000);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,
+			   STM32_LPTIM_CMPOKCF_ARROKCF);
+	if (ret)
+		return ret;
+
+	ret = clk_enable(priv->clk);
+	if (ret) {
+		regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
+		return ret;
+	}
+
+	/* Start LP timer in continuous mode */
+	return regmap_update_bits(priv->regmap, STM32_LPTIM_CR,
+				  STM32_LPTIM_CNTSTRT, STM32_LPTIM_CNTSTRT);
+}
+
+static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
+{
+	u32 mask = STM32_LPTIM_ENC | STM32_LPTIM_COUNTMODE |
+		   STM32_LPTIM_CKPOL | STM32_LPTIM_PRESC;
+	u32 val;
+
+	/* Setup LP timer encoder/counter and polarity, without prescaler */
+	if (priv->quadrature_mode)
+		val = enable ? STM32_LPTIM_ENC : 0;
+	else
+		val = enable ? STM32_LPTIM_COUNTMODE : 0;
+	val |= FIELD_PREP(STM32_LPTIM_CKPOL, enable ? priv->polarity : 0);
+
+	return regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask, val);
+}
+
+static int stm32_lptim_write_raw(struct iio_dev *indio_dev,
+				 struct iio_chan_spec const *chan,
+				 int val, int val2, long mask)
+{
+	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_ENABLE:
+		if (val < 0 || val > 1)
+			return -EINVAL;
+
+		/* Check nobody uses the timer, or already disabled/enabled */
+		ret = stm32_lptim_is_enabled(priv);
+		if ((ret < 0) || (!ret && !val))
+			return ret;
+		if (val && ret)
+			return -EBUSY;
+
+		ret = stm32_lptim_setup(priv, val);
+		if (ret)
+			return ret;
+		return stm32_lptim_set_enable_state(priv, val);
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int stm32_lptim_read_raw(struct iio_dev *indio_dev,
+				struct iio_chan_spec const *chan,
+				int *val, int *val2, long mask)
+{
+	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
+	u32 dat;
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = regmap_read(priv->regmap, STM32_LPTIM_CNT, &dat);
+		if (ret)
+			return ret;
+		*val = dat;
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_ENABLE:
+		ret = stm32_lptim_is_enabled(priv);
+		if (ret < 0)
+			return ret;
+		*val = ret;
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_SCALE:
+		/* Non-quadrature mode: scale = 1 */
+		*val = 1;
+		*val2 = 0;
+		if (priv->quadrature_mode) {
+			/*
+			 * Quadrature encoder mode:
+			 * - both edges, quarter cycle, scale is 0.25
+			 * - either rising/falling edge scale is 0.5
+			 */
+			if (priv->polarity > 1)
+				*val2 = 2;
+			else
+				*val2 = 1;
+		}
+		return IIO_VAL_FRACTIONAL_LOG2;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info stm32_lptim_cnt_iio_info = {
+	.read_raw = stm32_lptim_read_raw,
+	.write_raw = stm32_lptim_write_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static const char *const stm32_lptim_quadrature_modes[] = {
+	"non-quadrature",
+	"quadrature",
+};
+
+static int stm32_lptim_get_quadrature_mode(struct iio_dev *indio_dev,
+					   const struct iio_chan_spec *chan)
+{
+	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
+
+	return priv->quadrature_mode;
+}
+
+static int stm32_lptim_set_quadrature_mode(struct iio_dev *indio_dev,
+					   const struct iio_chan_spec *chan,
+					   unsigned int type)
+{
+	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
+
+	if (stm32_lptim_is_enabled(priv))
+		return -EBUSY;
+
+	priv->quadrature_mode = type;
+
+	return 0;
+}
+
+static const struct iio_enum stm32_lptim_quadrature_mode_en = {
+	.items = stm32_lptim_quadrature_modes,
+	.num_items = ARRAY_SIZE(stm32_lptim_quadrature_modes),
+	.get = stm32_lptim_get_quadrature_mode,
+	.set = stm32_lptim_set_quadrature_mode,
+};
+
+static const char * const stm32_lptim_cnt_polarity[] = {
+	"rising-edge", "falling-edge", "both-edges",
+};
+
+static int stm32_lptim_cnt_get_polarity(struct iio_dev *indio_dev,
+					const struct iio_chan_spec *chan)
+{
+	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
+
+	return priv->polarity;
+}
+
+static int stm32_lptim_cnt_set_polarity(struct iio_dev *indio_dev,
+					const struct iio_chan_spec *chan,
+					unsigned int type)
+{
+	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
+
+	if (stm32_lptim_is_enabled(priv))
+		return -EBUSY;
+
+	priv->polarity = type;
+
+	return 0;
+}
+
+static const struct iio_enum stm32_lptim_cnt_polarity_en = {
+	.items = stm32_lptim_cnt_polarity,
+	.num_items = ARRAY_SIZE(stm32_lptim_cnt_polarity),
+	.get = stm32_lptim_cnt_get_polarity,
+	.set = stm32_lptim_cnt_set_polarity,
+};
+
+static ssize_t stm32_lptim_cnt_get_preset(struct iio_dev *indio_dev,
+					  uintptr_t private,
+					  const struct iio_chan_spec *chan,
+					  char *buf)
+{
+	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset);
+}
+
+static ssize_t stm32_lptim_cnt_set_preset(struct iio_dev *indio_dev,
+					  uintptr_t private,
+					  const struct iio_chan_spec *chan,
+					  const char *buf, size_t len)
+{
+	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
+	int ret;
+
+	if (stm32_lptim_is_enabled(priv))
+		return -EBUSY;
+
+	ret = kstrtouint(buf, 0, &priv->preset);
+	if (ret)
+		return ret;
+
+	if (priv->preset > STM32_LPTIM_MAX_ARR)
+		return -EINVAL;
+
+	return len;
+}
+
+/* LP timer with encoder */
+static const struct iio_chan_spec_ext_info stm32_lptim_enc_ext_info[] = {
+	{
+		.name = "preset",
+		.shared = IIO_SEPARATE,
+		.read = stm32_lptim_cnt_get_preset,
+		.write = stm32_lptim_cnt_set_preset,
+	},
+	IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
+	IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
+	IIO_ENUM("quadrature_mode", IIO_SEPARATE,
+		 &stm32_lptim_quadrature_mode_en),
+	IIO_ENUM_AVAILABLE("quadrature_mode", &stm32_lptim_quadrature_mode_en),
+	{}
+};
+
+static const struct iio_chan_spec stm32_lptim_enc_channels = {
+	.type = IIO_COUNT,
+	.channel = 0,
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+			      BIT(IIO_CHAN_INFO_ENABLE) |
+			      BIT(IIO_CHAN_INFO_SCALE),
+	.ext_info = stm32_lptim_enc_ext_info,
+	.indexed = 1,
+};
+
+/* LP timer without encoder (counter only) */
+static const struct iio_chan_spec_ext_info stm32_lptim_cnt_ext_info[] = {
+	{
+		.name = "preset",
+		.shared = IIO_SEPARATE,
+		.read = stm32_lptim_cnt_get_preset,
+		.write = stm32_lptim_cnt_set_preset,
+	},
+	IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
+	IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
+	{}
+};
+
+static const struct iio_chan_spec stm32_lptim_cnt_channels = {
+	.type = IIO_COUNT,
+	.channel = 0,
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+			      BIT(IIO_CHAN_INFO_ENABLE) |
+			      BIT(IIO_CHAN_INFO_SCALE),
+	.ext_info = stm32_lptim_cnt_ext_info,
+	.indexed = 1,
+};
+
+static int stm32_lptim_cnt_probe(struct platform_device *pdev)
+{
+	struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
+	struct stm32_lptim_cnt *priv;
+	struct iio_dev *indio_dev;
+
+	if (IS_ERR_OR_NULL(ddata))
+		return -EINVAL;
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	priv = iio_priv(indio_dev);
+	priv->dev = &pdev->dev;
+	priv->regmap = ddata->regmap;
+	priv->clk = ddata->clk;
+	priv->preset = STM32_LPTIM_MAX_ARR;
+
+	indio_dev->name = dev_name(&pdev->dev);
+	indio_dev->dev.parent = &pdev->dev;
+	indio_dev->dev.of_node = pdev->dev.of_node;
+	indio_dev->info = &stm32_lptim_cnt_iio_info;
+	if (ddata->has_encoder)
+		indio_dev->channels = &stm32_lptim_enc_channels;
+	else
+		indio_dev->channels = &stm32_lptim_cnt_channels;
+	indio_dev->num_channels = 1;
+
+	platform_set_drvdata(pdev, priv);
+
+	return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+
+static const struct of_device_id stm32_lptim_cnt_of_match[] = {
+	{ .compatible = "st,stm32-lptimer-counter", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, stm32_lptim_cnt_of_match);
+
+static struct platform_driver stm32_lptim_cnt_driver = {
+	.probe = stm32_lptim_cnt_probe,
+	.driver = {
+		.name = "stm32-lptimer-counter",
+		.of_match_table = stm32_lptim_cnt_of_match,
+	},
+};
+module_platform_driver(stm32_lptim_cnt_driver);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_ALIAS("platform:stm32-lptimer-counter");
+MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 8/9] iio: counter: Add support for STM32 LPTimer
@ 2017-07-07 16:31   ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: linux-arm-kernel

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>
---
Changes in v2:
- s/Low Power/Low-Power
- update few comments
---
 .../ABI/testing/sysfs-bus-iio-lptimer-stm32        |  57 +++
 drivers/iio/counter/Kconfig                        |   9 +
 drivers/iio/counter/Makefile                       |   1 +
 drivers/iio/counter/stm32-lptimer-cnt.c            | 383 +++++++++++++++++++++
 4 files changed, 450 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
 create mode 100644 drivers/iio/counter/stm32-lptimer-cnt.c

diff --git a/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32 b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
new file mode 100644
index 0000000..ad2cc63
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
@@ -0,0 +1,57 @@
+What:		/sys/bus/iio/devices/iio:deviceX/in_count0_preset
+KernelVersion:	4.13
+Contact:	fabrice.gasnier at 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 at 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 at 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 at 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 at 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..474e1ac 100644
--- a/drivers/iio/counter/Kconfig
+++ b/drivers/iio/counter/Kconfig
@@ -21,4 +21,13 @@ config 104_QUAD_8
 	  The base port addresses for the devices may be configured via the base
 	  array module parameter.
 
+config STM32_LPTIMER_CNT
+	tristate "STM32 LP Timer encoder counter driver"
+	depends on MFD_STM32_LPTIMER || COMPILE_TEST
+	help
+	  Select this option to enable STM32 Low-Power Timer quadrature encoder
+	  and counter driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called stm32-lptimer-cnt.
 endmenu
diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
index 007e884..1b9a896 100644
--- a/drivers/iio/counter/Makefile
+++ b/drivers/iio/counter/Makefile
@@ -5,3 +5,4 @@
 # When adding new entries keep the list in alphabetical order
 
 obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
+obj-$(CONFIG_STM32_LPTIMER_CNT)	+= stm32-lptimer-cnt.o
diff --git a/drivers/iio/counter/stm32-lptimer-cnt.c b/drivers/iio/counter/stm32-lptimer-cnt.c
new file mode 100644
index 0000000..1c5909b
--- /dev/null
+++ b/drivers/iio/counter/stm32-lptimer-cnt.c
@@ -0,0 +1,383 @@
+/*
+ * STM32 Low-Power Timer Encoder and Counter driver
+ *
+ * Copyright (C) STMicroelectronics 2017
+ *
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>
+ *
+ * Inspired by 104-quad-8 and stm32-timer-trigger drivers.
+ *
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/bitfield.h>
+#include <linux/iio/iio.h>
+#include <linux/mfd/stm32-lptimer.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+struct stm32_lptim_cnt {
+	struct device *dev;
+	struct regmap *regmap;
+	struct clk *clk;
+	u32 preset;
+	u32 polarity;
+	u32 quadrature_mode;
+};
+
+static int stm32_lptim_is_enabled(struct stm32_lptim_cnt *priv)
+{
+	u32 val;
+	int ret;
+
+	ret = regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
+	if (ret)
+		return ret;
+
+	return FIELD_GET(STM32_LPTIM_ENABLE, val);
+}
+
+static int stm32_lptim_set_enable_state(struct stm32_lptim_cnt *priv,
+					int enable)
+{
+	int ret;
+	u32 val;
+
+	val = FIELD_PREP(STM32_LPTIM_ENABLE, enable);
+	ret = regmap_write(priv->regmap, STM32_LPTIM_CR, val);
+	if (ret)
+		return ret;
+
+	if (!enable) {
+		clk_disable(priv->clk);
+		return 0;
+	}
+
+	/* LP timer must be enabled before writing CMP & ARR */
+	ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, priv->preset);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, 0);
+	if (ret)
+		return ret;
+
+	/* ensure CMP & ARR registers are properly written */
+	ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val,
+				       (val & STM32_LPTIM_CMPOK_ARROK),
+				       100, 1000);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,
+			   STM32_LPTIM_CMPOKCF_ARROKCF);
+	if (ret)
+		return ret;
+
+	ret = clk_enable(priv->clk);
+	if (ret) {
+		regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
+		return ret;
+	}
+
+	/* Start LP timer in continuous mode */
+	return regmap_update_bits(priv->regmap, STM32_LPTIM_CR,
+				  STM32_LPTIM_CNTSTRT, STM32_LPTIM_CNTSTRT);
+}
+
+static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
+{
+	u32 mask = STM32_LPTIM_ENC | STM32_LPTIM_COUNTMODE |
+		   STM32_LPTIM_CKPOL | STM32_LPTIM_PRESC;
+	u32 val;
+
+	/* Setup LP timer encoder/counter and polarity, without prescaler */
+	if (priv->quadrature_mode)
+		val = enable ? STM32_LPTIM_ENC : 0;
+	else
+		val = enable ? STM32_LPTIM_COUNTMODE : 0;
+	val |= FIELD_PREP(STM32_LPTIM_CKPOL, enable ? priv->polarity : 0);
+
+	return regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask, val);
+}
+
+static int stm32_lptim_write_raw(struct iio_dev *indio_dev,
+				 struct iio_chan_spec const *chan,
+				 int val, int val2, long mask)
+{
+	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_ENABLE:
+		if (val < 0 || val > 1)
+			return -EINVAL;
+
+		/* Check nobody uses the timer, or already disabled/enabled */
+		ret = stm32_lptim_is_enabled(priv);
+		if ((ret < 0) || (!ret && !val))
+			return ret;
+		if (val && ret)
+			return -EBUSY;
+
+		ret = stm32_lptim_setup(priv, val);
+		if (ret)
+			return ret;
+		return stm32_lptim_set_enable_state(priv, val);
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int stm32_lptim_read_raw(struct iio_dev *indio_dev,
+				struct iio_chan_spec const *chan,
+				int *val, int *val2, long mask)
+{
+	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
+	u32 dat;
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = regmap_read(priv->regmap, STM32_LPTIM_CNT, &dat);
+		if (ret)
+			return ret;
+		*val = dat;
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_ENABLE:
+		ret = stm32_lptim_is_enabled(priv);
+		if (ret < 0)
+			return ret;
+		*val = ret;
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_SCALE:
+		/* Non-quadrature mode: scale = 1 */
+		*val = 1;
+		*val2 = 0;
+		if (priv->quadrature_mode) {
+			/*
+			 * Quadrature encoder mode:
+			 * - both edges, quarter cycle, scale is 0.25
+			 * - either rising/falling edge scale is 0.5
+			 */
+			if (priv->polarity > 1)
+				*val2 = 2;
+			else
+				*val2 = 1;
+		}
+		return IIO_VAL_FRACTIONAL_LOG2;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info stm32_lptim_cnt_iio_info = {
+	.read_raw = stm32_lptim_read_raw,
+	.write_raw = stm32_lptim_write_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static const char *const stm32_lptim_quadrature_modes[] = {
+	"non-quadrature",
+	"quadrature",
+};
+
+static int stm32_lptim_get_quadrature_mode(struct iio_dev *indio_dev,
+					   const struct iio_chan_spec *chan)
+{
+	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
+
+	return priv->quadrature_mode;
+}
+
+static int stm32_lptim_set_quadrature_mode(struct iio_dev *indio_dev,
+					   const struct iio_chan_spec *chan,
+					   unsigned int type)
+{
+	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
+
+	if (stm32_lptim_is_enabled(priv))
+		return -EBUSY;
+
+	priv->quadrature_mode = type;
+
+	return 0;
+}
+
+static const struct iio_enum stm32_lptim_quadrature_mode_en = {
+	.items = stm32_lptim_quadrature_modes,
+	.num_items = ARRAY_SIZE(stm32_lptim_quadrature_modes),
+	.get = stm32_lptim_get_quadrature_mode,
+	.set = stm32_lptim_set_quadrature_mode,
+};
+
+static const char * const stm32_lptim_cnt_polarity[] = {
+	"rising-edge", "falling-edge", "both-edges",
+};
+
+static int stm32_lptim_cnt_get_polarity(struct iio_dev *indio_dev,
+					const struct iio_chan_spec *chan)
+{
+	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
+
+	return priv->polarity;
+}
+
+static int stm32_lptim_cnt_set_polarity(struct iio_dev *indio_dev,
+					const struct iio_chan_spec *chan,
+					unsigned int type)
+{
+	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
+
+	if (stm32_lptim_is_enabled(priv))
+		return -EBUSY;
+
+	priv->polarity = type;
+
+	return 0;
+}
+
+static const struct iio_enum stm32_lptim_cnt_polarity_en = {
+	.items = stm32_lptim_cnt_polarity,
+	.num_items = ARRAY_SIZE(stm32_lptim_cnt_polarity),
+	.get = stm32_lptim_cnt_get_polarity,
+	.set = stm32_lptim_cnt_set_polarity,
+};
+
+static ssize_t stm32_lptim_cnt_get_preset(struct iio_dev *indio_dev,
+					  uintptr_t private,
+					  const struct iio_chan_spec *chan,
+					  char *buf)
+{
+	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset);
+}
+
+static ssize_t stm32_lptim_cnt_set_preset(struct iio_dev *indio_dev,
+					  uintptr_t private,
+					  const struct iio_chan_spec *chan,
+					  const char *buf, size_t len)
+{
+	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
+	int ret;
+
+	if (stm32_lptim_is_enabled(priv))
+		return -EBUSY;
+
+	ret = kstrtouint(buf, 0, &priv->preset);
+	if (ret)
+		return ret;
+
+	if (priv->preset > STM32_LPTIM_MAX_ARR)
+		return -EINVAL;
+
+	return len;
+}
+
+/* LP timer with encoder */
+static const struct iio_chan_spec_ext_info stm32_lptim_enc_ext_info[] = {
+	{
+		.name = "preset",
+		.shared = IIO_SEPARATE,
+		.read = stm32_lptim_cnt_get_preset,
+		.write = stm32_lptim_cnt_set_preset,
+	},
+	IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
+	IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
+	IIO_ENUM("quadrature_mode", IIO_SEPARATE,
+		 &stm32_lptim_quadrature_mode_en),
+	IIO_ENUM_AVAILABLE("quadrature_mode", &stm32_lptim_quadrature_mode_en),
+	{}
+};
+
+static const struct iio_chan_spec stm32_lptim_enc_channels = {
+	.type = IIO_COUNT,
+	.channel = 0,
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+			      BIT(IIO_CHAN_INFO_ENABLE) |
+			      BIT(IIO_CHAN_INFO_SCALE),
+	.ext_info = stm32_lptim_enc_ext_info,
+	.indexed = 1,
+};
+
+/* LP timer without encoder (counter only) */
+static const struct iio_chan_spec_ext_info stm32_lptim_cnt_ext_info[] = {
+	{
+		.name = "preset",
+		.shared = IIO_SEPARATE,
+		.read = stm32_lptim_cnt_get_preset,
+		.write = stm32_lptim_cnt_set_preset,
+	},
+	IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
+	IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
+	{}
+};
+
+static const struct iio_chan_spec stm32_lptim_cnt_channels = {
+	.type = IIO_COUNT,
+	.channel = 0,
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+			      BIT(IIO_CHAN_INFO_ENABLE) |
+			      BIT(IIO_CHAN_INFO_SCALE),
+	.ext_info = stm32_lptim_cnt_ext_info,
+	.indexed = 1,
+};
+
+static int stm32_lptim_cnt_probe(struct platform_device *pdev)
+{
+	struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
+	struct stm32_lptim_cnt *priv;
+	struct iio_dev *indio_dev;
+
+	if (IS_ERR_OR_NULL(ddata))
+		return -EINVAL;
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	priv = iio_priv(indio_dev);
+	priv->dev = &pdev->dev;
+	priv->regmap = ddata->regmap;
+	priv->clk = ddata->clk;
+	priv->preset = STM32_LPTIM_MAX_ARR;
+
+	indio_dev->name = dev_name(&pdev->dev);
+	indio_dev->dev.parent = &pdev->dev;
+	indio_dev->dev.of_node = pdev->dev.of_node;
+	indio_dev->info = &stm32_lptim_cnt_iio_info;
+	if (ddata->has_encoder)
+		indio_dev->channels = &stm32_lptim_enc_channels;
+	else
+		indio_dev->channels = &stm32_lptim_cnt_channels;
+	indio_dev->num_channels = 1;
+
+	platform_set_drvdata(pdev, priv);
+
+	return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+
+static const struct of_device_id stm32_lptim_cnt_of_match[] = {
+	{ .compatible = "st,stm32-lptimer-counter", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, stm32_lptim_cnt_of_match);
+
+static struct platform_driver stm32_lptim_cnt_driver = {
+	.probe = stm32_lptim_cnt_probe,
+	.driver = {
+		.name = "stm32-lptimer-counter",
+		.of_match_table = stm32_lptim_cnt_of_match,
+	},
+};
+module_platform_driver(stm32_lptim_cnt_driver);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_ALIAS("platform:stm32-lptimer-counter");
+MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 9/9] iio: adc: stm32: add support for lptimer triggers
@ 2017-07-07 16:31   ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: lee.jones, benjamin.gaignard, jic23, thierry.reding, robh+dt
  Cc: mark.rutland, alexandre.torgue, mcoquelin.stm32, fabrice.gasnier,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm

STM32 ADC supports hardware triggers like STM32 Low-Power Timer.
For instance, STM32H7 ADC may be triggered by instances 1, 2 or 3.
Add hardware triggered mode so Low-Power Timer Trigger driver can
validate device.

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
 drivers/iio/adc/stm32-adc.c | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 5bfcc1f..1147d7e 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -25,6 +25,7 @@
 #include <linux/dmaengine.h>
 #include <linux/iio/iio.h>
 #include <linux/iio/buffer.h>
+#include <linux/iio/timer/stm32-lptim-trigger.h>
 #include <linux/iio/timer/stm32-timer-trigger.h>
 #include <linux/iio/trigger.h>
 #include <linux/iio/trigger_consumer.h>
@@ -182,6 +183,11 @@ enum stm32_adc_extsel {
 	STM32_EXT13,
 	STM32_EXT14,
 	STM32_EXT15,
+	STM32_EXT16,
+	STM32_EXT17,
+	STM32_EXT18,
+	STM32_EXT19,
+	STM32_EXT20,
 };
 
 /**
@@ -480,6 +486,9 @@ struct stm32_adc_info {
 	{ TIM4_TRGO, STM32_EXT12 },
 	{ TIM6_TRGO, STM32_EXT13 },
 	{ TIM3_CH4, STM32_EXT15 },
+	{ LPTIM1_OUT, STM32_EXT18 },
+	{ LPTIM2_OUT, STM32_EXT19 },
+	{ LPTIM3_OUT, STM32_EXT20 },
 	{},
 };
 
@@ -995,7 +1004,8 @@ static int stm32_adc_get_trig_extsel(struct iio_dev *indio_dev,
 		 * Checking both stm32 timer trigger type and trig name
 		 * should be safe against arbitrary trigger names.
 		 */
-		if (is_stm32_timer_trigger(trig) &&
+		if ((is_stm32_timer_trigger(trig) ||
+		     is_stm32_lptim_trigger(trig)) &&
 		    !strcmp(adc->cfg->trigs[i].name, trig->name)) {
 			return adc->cfg->trigs[i].extsel;
 		}
@@ -1634,7 +1644,7 @@ static int stm32_adc_probe(struct platform_device *pdev)
 	indio_dev->dev.parent = &pdev->dev;
 	indio_dev->dev.of_node = pdev->dev.of_node;
 	indio_dev->info = &stm32_adc_iio_info;
-	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->modes = INDIO_DIRECT_MODE | INDIO_HARDWARE_TRIGGERED;
 
 	platform_set_drvdata(pdev, adc);
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 9/9] iio: adc: stm32: add support for lptimer triggers
@ 2017-07-07 16:31   ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: lee.jones-QSEj5FYQhm4dnm+yROfE0A,
	benjamin.gaignard-QSEj5FYQhm4dnm+yROfE0A,
	jic23-DgEjT+Ai2ygdnm+yROfE0A,
	thierry.reding-Re5JQEeQqe8AvxtiuMwx3w,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A
  Cc: mark.rutland-5wv7dgnIgG8, alexandre.torgue-qxv4g6HH51o,
	mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
	fabrice.gasnier-qxv4g6HH51o, benjamin.gaignard-qxv4g6HH51o,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-pwm-u79uwXL29TY76Z2rM5mHXA

STM32 ADC supports hardware triggers like STM32 Low-Power Timer.
For instance, STM32H7 ADC may be triggered by instances 1, 2 or 3.
Add hardware triggered mode so Low-Power Timer Trigger driver can
validate device.

Signed-off-by: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
---
 drivers/iio/adc/stm32-adc.c | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 5bfcc1f..1147d7e 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -25,6 +25,7 @@
 #include <linux/dmaengine.h>
 #include <linux/iio/iio.h>
 #include <linux/iio/buffer.h>
+#include <linux/iio/timer/stm32-lptim-trigger.h>
 #include <linux/iio/timer/stm32-timer-trigger.h>
 #include <linux/iio/trigger.h>
 #include <linux/iio/trigger_consumer.h>
@@ -182,6 +183,11 @@ enum stm32_adc_extsel {
 	STM32_EXT13,
 	STM32_EXT14,
 	STM32_EXT15,
+	STM32_EXT16,
+	STM32_EXT17,
+	STM32_EXT18,
+	STM32_EXT19,
+	STM32_EXT20,
 };
 
 /**
@@ -480,6 +486,9 @@ struct stm32_adc_info {
 	{ TIM4_TRGO, STM32_EXT12 },
 	{ TIM6_TRGO, STM32_EXT13 },
 	{ TIM3_CH4, STM32_EXT15 },
+	{ LPTIM1_OUT, STM32_EXT18 },
+	{ LPTIM2_OUT, STM32_EXT19 },
+	{ LPTIM3_OUT, STM32_EXT20 },
 	{},
 };
 
@@ -995,7 +1004,8 @@ static int stm32_adc_get_trig_extsel(struct iio_dev *indio_dev,
 		 * Checking both stm32 timer trigger type and trig name
 		 * should be safe against arbitrary trigger names.
 		 */
-		if (is_stm32_timer_trigger(trig) &&
+		if ((is_stm32_timer_trigger(trig) ||
+		     is_stm32_lptim_trigger(trig)) &&
 		    !strcmp(adc->cfg->trigs[i].name, trig->name)) {
 			return adc->cfg->trigs[i].extsel;
 		}
@@ -1634,7 +1644,7 @@ static int stm32_adc_probe(struct platform_device *pdev)
 	indio_dev->dev.parent = &pdev->dev;
 	indio_dev->dev.of_node = pdev->dev.of_node;
 	indio_dev->info = &stm32_adc_iio_info;
-	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->modes = INDIO_DIRECT_MODE | INDIO_HARDWARE_TRIGGERED;
 
 	platform_set_drvdata(pdev, adc);
 
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v3 9/9] iio: adc: stm32: add support for lptimer triggers
@ 2017-07-07 16:31   ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-07 16:31 UTC (permalink / raw)
  To: linux-arm-kernel

STM32 ADC supports hardware triggers like STM32 Low-Power Timer.
For instance, STM32H7 ADC may be triggered by instances 1, 2 or 3.
Add hardware triggered mode so Low-Power Timer Trigger driver can
validate device.

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
 drivers/iio/adc/stm32-adc.c | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 5bfcc1f..1147d7e 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -25,6 +25,7 @@
 #include <linux/dmaengine.h>
 #include <linux/iio/iio.h>
 #include <linux/iio/buffer.h>
+#include <linux/iio/timer/stm32-lptim-trigger.h>
 #include <linux/iio/timer/stm32-timer-trigger.h>
 #include <linux/iio/trigger.h>
 #include <linux/iio/trigger_consumer.h>
@@ -182,6 +183,11 @@ enum stm32_adc_extsel {
 	STM32_EXT13,
 	STM32_EXT14,
 	STM32_EXT15,
+	STM32_EXT16,
+	STM32_EXT17,
+	STM32_EXT18,
+	STM32_EXT19,
+	STM32_EXT20,
 };
 
 /**
@@ -480,6 +486,9 @@ struct stm32_adc_info {
 	{ TIM4_TRGO, STM32_EXT12 },
 	{ TIM6_TRGO, STM32_EXT13 },
 	{ TIM3_CH4, STM32_EXT15 },
+	{ LPTIM1_OUT, STM32_EXT18 },
+	{ LPTIM2_OUT, STM32_EXT19 },
+	{ LPTIM3_OUT, STM32_EXT20 },
 	{},
 };
 
@@ -995,7 +1004,8 @@ static int stm32_adc_get_trig_extsel(struct iio_dev *indio_dev,
 		 * Checking both stm32 timer trigger type and trig name
 		 * should be safe against arbitrary trigger names.
 		 */
-		if (is_stm32_timer_trigger(trig) &&
+		if ((is_stm32_timer_trigger(trig) ||
+		     is_stm32_lptim_trigger(trig)) &&
 		    !strcmp(adc->cfg->trigs[i].name, trig->name)) {
 			return adc->cfg->trigs[i].extsel;
 		}
@@ -1634,7 +1644,7 @@ static int stm32_adc_probe(struct platform_device *pdev)
 	indio_dev->dev.parent = &pdev->dev;
 	indio_dev->dev.of_node = pdev->dev.of_node;
 	indio_dev->info = &stm32_adc_iio_info;
-	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->modes = INDIO_DIRECT_MODE | INDIO_HARDWARE_TRIGGERED;
 
 	platform_set_drvdata(pdev, adc);
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 6/9] iio: trigger: Add STM32 LPTimer trigger driver
  2017-07-07 16:31   ` Fabrice Gasnier
  (?)
@ 2017-07-09 20:04     ` Jonathan Cameron
  -1 siblings, 0 replies; 63+ messages in thread
From: Jonathan Cameron @ 2017-07-09 20:04 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: lee.jones, benjamin.gaignard, thierry.reding, robh+dt,
	mark.rutland, alexandre.torgue, mcoquelin.stm32,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm

On Fri, 7 Jul 2017 18:31:05 +0200
Fabrice Gasnier <fabrice.gasnier@st.com> wrote:

> Add support for LPTIMx_OUT triggers that can be found on some STM32
> devices. These triggers can be used then by ADC or DAC.
> Typical usage is to configure LPTimer as PWM output (via pwm-stm32-lp)
> and have synchronised analog conversions with these triggers.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

Looks good to me. I'm assuming this will ultimately go through mfd
so let me know if anything else is planned.

Thanks,

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

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 6/9] iio: trigger: Add STM32 LPTimer trigger driver
@ 2017-07-09 20:04     ` Jonathan Cameron
  0 siblings, 0 replies; 63+ messages in thread
From: Jonathan Cameron @ 2017-07-09 20:04 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: lee.jones, benjamin.gaignard, thierry.reding, robh+dt,
	mark.rutland, alexandre.torgue, mcoquelin.stm32,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm

On Fri, 7 Jul 2017 18:31:05 +0200
Fabrice Gasnier <fabrice.gasnier@st.com> wrote:

> Add support for LPTIMx_OUT triggers that can be found on some STM32
> devices. These triggers can be used then by ADC or DAC.
> Typical usage is to configure LPTimer as PWM output (via pwm-stm32-lp)
> and have synchronised analog conversions with these triggers.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

Looks good to me. I'm assuming this will ultimately go through mfd
so let me know if anything else is planned.

Thanks,

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

^ permalink raw reply	[flat|nested] 63+ messages in thread

* [PATCH v3 6/9] iio: trigger: Add STM32 LPTimer trigger driver
@ 2017-07-09 20:04     ` Jonathan Cameron
  0 siblings, 0 replies; 63+ messages in thread
From: Jonathan Cameron @ 2017-07-09 20:04 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, 7 Jul 2017 18:31:05 +0200
Fabrice Gasnier <fabrice.gasnier@st.com> wrote:

> Add support for LPTIMx_OUT triggers that can be found on some STM32
> devices. These triggers can be used then by ADC or DAC.
> Typical usage is to configure LPTimer as PWM output (via pwm-stm32-lp)
> and have synchronised analog conversions with these triggers.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

Looks good to me. I'm assuming this will ultimately go through mfd
so let me know if anything else is planned.

Thanks,

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

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 8/9] iio: counter: Add support for STM32 LPTimer
  2017-07-07 16:31   ` Fabrice Gasnier
  (?)
  (?)
@ 2017-07-09 20:12     ` Jonathan Cameron
  -1 siblings, 0 replies; 63+ messages in thread
From: Jonathan Cameron @ 2017-07-09 20:12 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: lee.jones, benjamin.gaignard, thierry.reding, robh+dt,
	mark.rutland, alexandre.torgue, mcoquelin.stm32,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm, William Breathitt Gray

On Fri, 7 Jul 2017 18:31:07 +0200
Fabrice Gasnier <fabrice.gasnier@st.com> wrote:

> 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>
So the obvious question on this one is whether to to delay merging
until we have come to some decision on William's proposed new interface.

My gut feeling is no - we are supporting this interface anyway going
forward for a couple of devices.  Even if the new interface comes
along reasonably quickly I don't have any problem supporting this
approach for one more driver.

So I'd like to let this sit for a little while longer ideally for other
opinions.

>From me however it is good to go.

Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> ---
> Changes in v2:
> - s/Low Power/Low-Power
> - update few comments
> ---
>  .../ABI/testing/sysfs-bus-iio-lptimer-stm32        |  57 +++
>  drivers/iio/counter/Kconfig                        |   9 +
>  drivers/iio/counter/Makefile                       |   1 +
>  drivers/iio/counter/stm32-lptimer-cnt.c            | 383 +++++++++++++++++++++
>  4 files changed, 450 insertions(+)
>  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
>  create mode 100644 drivers/iio/counter/stm32-lptimer-cnt.c
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32 b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
> new file mode 100644
> index 0000000..ad2cc63
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
> @@ -0,0 +1,57 @@
> +What:		/sys/bus/iio/devices/iio:deviceX/in_count0_preset
> +KernelVersion:	4.13
> +Contact:	fabrice.gasnier@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
So this is our one oddity.  I'm happy enough with the interface until if
we come up with something better.  So lets go with this for now.
> +
> +		In non-quadrature mode, device counts up on active edge.
> +		In quadrature mode, encoder counting scenarios are as follows:
> +		----------------------------------------------------------------
> +		| Active  | Level on |      IN1 signal    |     IN2 signal     |
> +		| edge    | opposite |------------------------------------------
> +		|         | signal   |  Rising  | Falling |  Rising  | Falling |
> +		----------------------------------------------------------------
> +		| Rising  | High ->  |   Down   |    -    |    Up    |    -    |
> +		| edge    | Low  ->  |    Up    |    -    |   Down   |    -    |
> +		----------------------------------------------------------------
> +		| Falling | High ->  |    -     |    Up   |    -     |   Down  |
> +		| edge    | Low  ->  |    -     |   Down  |    -     |    Up   |
> +		----------------------------------------------------------------
> +		| Both    | High ->  |   Down   |    Up   |    Up    |   Down  |
> +		| edges   | Low  ->  |    Up    |   Down  |   Down   |    Up   |
> +		----------------------------------------------------------------
> diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
> index b37e5fc..474e1ac 100644
> --- a/drivers/iio/counter/Kconfig
> +++ b/drivers/iio/counter/Kconfig
> @@ -21,4 +21,13 @@ config 104_QUAD_8
>  	  The base port addresses for the devices may be configured via the base
>  	  array module parameter.
>  
> +config STM32_LPTIMER_CNT
> +	tristate "STM32 LP Timer encoder counter driver"
> +	depends on MFD_STM32_LPTIMER || COMPILE_TEST
> +	help
> +	  Select this option to enable STM32 Low-Power Timer quadrature encoder
> +	  and counter driver.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called stm32-lptimer-cnt.
>  endmenu
> diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
> index 007e884..1b9a896 100644
> --- a/drivers/iio/counter/Makefile
> +++ b/drivers/iio/counter/Makefile
> @@ -5,3 +5,4 @@
>  # When adding new entries keep the list in alphabetical order
>  
>  obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
> +obj-$(CONFIG_STM32_LPTIMER_CNT)	+= stm32-lptimer-cnt.o
> diff --git a/drivers/iio/counter/stm32-lptimer-cnt.c b/drivers/iio/counter/stm32-lptimer-cnt.c
> new file mode 100644
> index 0000000..1c5909b
> --- /dev/null
> +++ b/drivers/iio/counter/stm32-lptimer-cnt.c
> @@ -0,0 +1,383 @@
> +/*
> + * STM32 Low-Power Timer Encoder and Counter driver
> + *
> + * Copyright (C) STMicroelectronics 2017
> + *
> + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>
> + *
> + * Inspired by 104-quad-8 and stm32-timer-trigger drivers.
> + *
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/iio/iio.h>
> +#include <linux/mfd/stm32-lptimer.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +
> +struct stm32_lptim_cnt {
> +	struct device *dev;
> +	struct regmap *regmap;
> +	struct clk *clk;
> +	u32 preset;
> +	u32 polarity;
> +	u32 quadrature_mode;
> +};
> +
> +static int stm32_lptim_is_enabled(struct stm32_lptim_cnt *priv)
> +{
> +	u32 val;
> +	int ret;
> +
> +	ret = regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
> +	if (ret)
> +		return ret;
> +
> +	return FIELD_GET(STM32_LPTIM_ENABLE, val);
> +}
> +
> +static int stm32_lptim_set_enable_state(struct stm32_lptim_cnt *priv,
> +					int enable)
> +{
> +	int ret;
> +	u32 val;
> +
> +	val = FIELD_PREP(STM32_LPTIM_ENABLE, enable);
> +	ret = regmap_write(priv->regmap, STM32_LPTIM_CR, val);
> +	if (ret)
> +		return ret;
> +
> +	if (!enable) {
> +		clk_disable(priv->clk);
> +		return 0;
> +	}
> +
> +	/* LP timer must be enabled before writing CMP & ARR */
> +	ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, priv->preset);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, 0);
> +	if (ret)
> +		return ret;
> +
> +	/* ensure CMP & ARR registers are properly written */
> +	ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val,
> +				       (val & STM32_LPTIM_CMPOK_ARROK),
> +				       100, 1000);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,
> +			   STM32_LPTIM_CMPOKCF_ARROKCF);
> +	if (ret)
> +		return ret;
> +
> +	ret = clk_enable(priv->clk);
> +	if (ret) {
> +		regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
> +		return ret;
> +	}
> +
> +	/* Start LP timer in continuous mode */
> +	return regmap_update_bits(priv->regmap, STM32_LPTIM_CR,
> +				  STM32_LPTIM_CNTSTRT, STM32_LPTIM_CNTSTRT);
> +}
> +
> +static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
> +{
> +	u32 mask = STM32_LPTIM_ENC | STM32_LPTIM_COUNTMODE |
> +		   STM32_LPTIM_CKPOL | STM32_LPTIM_PRESC;
> +	u32 val;
> +
> +	/* Setup LP timer encoder/counter and polarity, without prescaler */
> +	if (priv->quadrature_mode)
> +		val = enable ? STM32_LPTIM_ENC : 0;
> +	else
> +		val = enable ? STM32_LPTIM_COUNTMODE : 0;
> +	val |= FIELD_PREP(STM32_LPTIM_CKPOL, enable ? priv->polarity : 0);
> +
> +	return regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask, val);
> +}
> +
> +static int stm32_lptim_write_raw(struct iio_dev *indio_dev,
> +				 struct iio_chan_spec const *chan,
> +				 int val, int val2, long mask)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_ENABLE:
> +		if (val < 0 || val > 1)
> +			return -EINVAL;
> +
> +		/* Check nobody uses the timer, or already disabled/enabled */
> +		ret = stm32_lptim_is_enabled(priv);
> +		if ((ret < 0) || (!ret && !val))
> +			return ret;
> +		if (val && ret)
> +			return -EBUSY;
> +
> +		ret = stm32_lptim_setup(priv, val);
> +		if (ret)
> +			return ret;
> +		return stm32_lptim_set_enable_state(priv, val);
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int stm32_lptim_read_raw(struct iio_dev *indio_dev,
> +				struct iio_chan_spec const *chan,
> +				int *val, int *val2, long mask)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +	u32 dat;
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = regmap_read(priv->regmap, STM32_LPTIM_CNT, &dat);
> +		if (ret)
> +			return ret;
> +		*val = dat;
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_ENABLE:
> +		ret = stm32_lptim_is_enabled(priv);
> +		if (ret < 0)
> +			return ret;
> +		*val = ret;
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_SCALE:
> +		/* Non-quadrature mode: scale = 1 */
> +		*val = 1;
> +		*val2 = 0;
> +		if (priv->quadrature_mode) {
> +			/*
> +			 * Quadrature encoder mode:
> +			 * - both edges, quarter cycle, scale is 0.25
> +			 * - either rising/falling edge scale is 0.5
> +			 */
> +			if (priv->polarity > 1)
> +				*val2 = 2;
> +			else
> +				*val2 = 1;
> +		}
> +		return IIO_VAL_FRACTIONAL_LOG2;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static const struct iio_info stm32_lptim_cnt_iio_info = {
> +	.read_raw = stm32_lptim_read_raw,
> +	.write_raw = stm32_lptim_write_raw,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static const char *const stm32_lptim_quadrature_modes[] = {
> +	"non-quadrature",
> +	"quadrature",
> +};
> +
> +static int stm32_lptim_get_quadrature_mode(struct iio_dev *indio_dev,
> +					   const struct iio_chan_spec *chan)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +
> +	return priv->quadrature_mode;
> +}
> +
> +static int stm32_lptim_set_quadrature_mode(struct iio_dev *indio_dev,
> +					   const struct iio_chan_spec *chan,
> +					   unsigned int type)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +
> +	if (stm32_lptim_is_enabled(priv))
> +		return -EBUSY;
> +
> +	priv->quadrature_mode = type;
> +
> +	return 0;
> +}
> +
> +static const struct iio_enum stm32_lptim_quadrature_mode_en = {
> +	.items = stm32_lptim_quadrature_modes,
> +	.num_items = ARRAY_SIZE(stm32_lptim_quadrature_modes),
> +	.get = stm32_lptim_get_quadrature_mode,
> +	.set = stm32_lptim_set_quadrature_mode,
> +};
> +
> +static const char * const stm32_lptim_cnt_polarity[] = {
> +	"rising-edge", "falling-edge", "both-edges",
> +};
> +
> +static int stm32_lptim_cnt_get_polarity(struct iio_dev *indio_dev,
> +					const struct iio_chan_spec *chan)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +
> +	return priv->polarity;
> +}
> +
> +static int stm32_lptim_cnt_set_polarity(struct iio_dev *indio_dev,
> +					const struct iio_chan_spec *chan,
> +					unsigned int type)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +
> +	if (stm32_lptim_is_enabled(priv))
> +		return -EBUSY;
> +
> +	priv->polarity = type;
> +
> +	return 0;
> +}
> +
> +static const struct iio_enum stm32_lptim_cnt_polarity_en = {
> +	.items = stm32_lptim_cnt_polarity,
> +	.num_items = ARRAY_SIZE(stm32_lptim_cnt_polarity),
> +	.get = stm32_lptim_cnt_get_polarity,
> +	.set = stm32_lptim_cnt_set_polarity,
> +};
> +
> +static ssize_t stm32_lptim_cnt_get_preset(struct iio_dev *indio_dev,
> +					  uintptr_t private,
> +					  const struct iio_chan_spec *chan,
> +					  char *buf)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset);
> +}
> +
> +static ssize_t stm32_lptim_cnt_set_preset(struct iio_dev *indio_dev,
> +					  uintptr_t private,
> +					  const struct iio_chan_spec *chan,
> +					  const char *buf, size_t len)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +	int ret;
> +
> +	if (stm32_lptim_is_enabled(priv))
> +		return -EBUSY;
> +
> +	ret = kstrtouint(buf, 0, &priv->preset);
> +	if (ret)
> +		return ret;
> +
> +	if (priv->preset > STM32_LPTIM_MAX_ARR)
> +		return -EINVAL;
> +
> +	return len;
> +}
> +
> +/* LP timer with encoder */
> +static const struct iio_chan_spec_ext_info stm32_lptim_enc_ext_info[] = {
> +	{
> +		.name = "preset",
> +		.shared = IIO_SEPARATE,
> +		.read = stm32_lptim_cnt_get_preset,
> +		.write = stm32_lptim_cnt_set_preset,
> +	},
> +	IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
> +	IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
> +	IIO_ENUM("quadrature_mode", IIO_SEPARATE,
> +		 &stm32_lptim_quadrature_mode_en),
> +	IIO_ENUM_AVAILABLE("quadrature_mode", &stm32_lptim_quadrature_mode_en),
> +	{}
> +};
> +
> +static const struct iio_chan_spec stm32_lptim_enc_channels = {
> +	.type = IIO_COUNT,
> +	.channel = 0,
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +			      BIT(IIO_CHAN_INFO_ENABLE) |
> +			      BIT(IIO_CHAN_INFO_SCALE),
> +	.ext_info = stm32_lptim_enc_ext_info,
> +	.indexed = 1,
> +};
> +
> +/* LP timer without encoder (counter only) */
> +static const struct iio_chan_spec_ext_info stm32_lptim_cnt_ext_info[] = {
> +	{
> +		.name = "preset",
> +		.shared = IIO_SEPARATE,
> +		.read = stm32_lptim_cnt_get_preset,
> +		.write = stm32_lptim_cnt_set_preset,
> +	},
> +	IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
> +	IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
> +	{}
> +};
> +
> +static const struct iio_chan_spec stm32_lptim_cnt_channels = {
> +	.type = IIO_COUNT,
> +	.channel = 0,
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +			      BIT(IIO_CHAN_INFO_ENABLE) |
> +			      BIT(IIO_CHAN_INFO_SCALE),
> +	.ext_info = stm32_lptim_cnt_ext_info,
> +	.indexed = 1,
> +};
> +
> +static int stm32_lptim_cnt_probe(struct platform_device *pdev)
> +{
> +	struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
> +	struct stm32_lptim_cnt *priv;
> +	struct iio_dev *indio_dev;
> +
> +	if (IS_ERR_OR_NULL(ddata))
> +		return -EINVAL;
> +
> +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	priv = iio_priv(indio_dev);
> +	priv->dev = &pdev->dev;
> +	priv->regmap = ddata->regmap;
> +	priv->clk = ddata->clk;
> +	priv->preset = STM32_LPTIM_MAX_ARR;
> +
> +	indio_dev->name = dev_name(&pdev->dev);
> +	indio_dev->dev.parent = &pdev->dev;
> +	indio_dev->dev.of_node = pdev->dev.of_node;
> +	indio_dev->info = &stm32_lptim_cnt_iio_info;
> +	if (ddata->has_encoder)
> +		indio_dev->channels = &stm32_lptim_enc_channels;
> +	else
> +		indio_dev->channels = &stm32_lptim_cnt_channels;
> +	indio_dev->num_channels = 1;
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	return devm_iio_device_register(&pdev->dev, indio_dev);
> +}
> +
> +static const struct of_device_id stm32_lptim_cnt_of_match[] = {
> +	{ .compatible = "st,stm32-lptimer-counter", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, stm32_lptim_cnt_of_match);
> +
> +static struct platform_driver stm32_lptim_cnt_driver = {
> +	.probe = stm32_lptim_cnt_probe,
> +	.driver = {
> +		.name = "stm32-lptimer-counter",
> +		.of_match_table = stm32_lptim_cnt_of_match,
> +	},
> +};
> +module_platform_driver(stm32_lptim_cnt_driver);
> +
> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
> +MODULE_ALIAS("platform:stm32-lptimer-counter");
> +MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
> +MODULE_LICENSE("GPL v2");

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 8/9] iio: counter: Add support for STM32 LPTimer
@ 2017-07-09 20:12     ` Jonathan Cameron
  0 siblings, 0 replies; 63+ messages in thread
From: Jonathan Cameron @ 2017-07-09 20:12 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: lee.jones, benjamin.gaignard, thierry.reding, robh+dt,
	mark.rutland, alexandre.torgue, mcoquelin.stm32,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm, William Breathitt Gray

On Fri, 7 Jul 2017 18:31:07 +0200
Fabrice Gasnier <fabrice.gasnier@st.com> wrote:

> 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>
So the obvious question on this one is whether to to delay merging
until we have come to some decision on William's proposed new interface.

My gut feeling is no - we are supporting this interface anyway going
forward for a couple of devices.  Even if the new interface comes
along reasonably quickly I don't have any problem supporting this
approach for one more driver.

So I'd like to let this sit for a little while longer ideally for other
opinions.

>From me however it is good to go.

Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> ---
> Changes in v2:
> - s/Low Power/Low-Power
> - update few comments
> ---
>  .../ABI/testing/sysfs-bus-iio-lptimer-stm32        |  57 +++
>  drivers/iio/counter/Kconfig                        |   9 +
>  drivers/iio/counter/Makefile                       |   1 +
>  drivers/iio/counter/stm32-lptimer-cnt.c            | 383 +++++++++++++++++++++
>  4 files changed, 450 insertions(+)
>  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
>  create mode 100644 drivers/iio/counter/stm32-lptimer-cnt.c
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32 b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
> new file mode 100644
> index 0000000..ad2cc63
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
> @@ -0,0 +1,57 @@
> +What:		/sys/bus/iio/devices/iio:deviceX/in_count0_preset
> +KernelVersion:	4.13
> +Contact:	fabrice.gasnier@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
So this is our one oddity.  I'm happy enough with the interface until if
we come up with something better.  So lets go with this for now.
> +
> +		In non-quadrature mode, device counts up on active edge.
> +		In quadrature mode, encoder counting scenarios are as follows:
> +		----------------------------------------------------------------
> +		| Active  | Level on |      IN1 signal    |     IN2 signal     |
> +		| edge    | opposite |------------------------------------------
> +		|         | signal   |  Rising  | Falling |  Rising  | Falling |
> +		----------------------------------------------------------------
> +		| Rising  | High ->  |   Down   |    -    |    Up    |    -    |
> +		| edge    | Low  ->  |    Up    |    -    |   Down   |    -    |
> +		----------------------------------------------------------------
> +		| Falling | High ->  |    -     |    Up   |    -     |   Down  |
> +		| edge    | Low  ->  |    -     |   Down  |    -     |    Up   |
> +		----------------------------------------------------------------
> +		| Both    | High ->  |   Down   |    Up   |    Up    |   Down  |
> +		| edges   | Low  ->  |    Up    |   Down  |   Down   |    Up   |
> +		----------------------------------------------------------------
> diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
> index b37e5fc..474e1ac 100644
> --- a/drivers/iio/counter/Kconfig
> +++ b/drivers/iio/counter/Kconfig
> @@ -21,4 +21,13 @@ config 104_QUAD_8
>  	  The base port addresses for the devices may be configured via the base
>  	  array module parameter.
>  
> +config STM32_LPTIMER_CNT
> +	tristate "STM32 LP Timer encoder counter driver"
> +	depends on MFD_STM32_LPTIMER || COMPILE_TEST
> +	help
> +	  Select this option to enable STM32 Low-Power Timer quadrature encoder
> +	  and counter driver.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called stm32-lptimer-cnt.
>  endmenu
> diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
> index 007e884..1b9a896 100644
> --- a/drivers/iio/counter/Makefile
> +++ b/drivers/iio/counter/Makefile
> @@ -5,3 +5,4 @@
>  # When adding new entries keep the list in alphabetical order
>  
>  obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
> +obj-$(CONFIG_STM32_LPTIMER_CNT)	+= stm32-lptimer-cnt.o
> diff --git a/drivers/iio/counter/stm32-lptimer-cnt.c b/drivers/iio/counter/stm32-lptimer-cnt.c
> new file mode 100644
> index 0000000..1c5909b
> --- /dev/null
> +++ b/drivers/iio/counter/stm32-lptimer-cnt.c
> @@ -0,0 +1,383 @@
> +/*
> + * STM32 Low-Power Timer Encoder and Counter driver
> + *
> + * Copyright (C) STMicroelectronics 2017
> + *
> + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>
> + *
> + * Inspired by 104-quad-8 and stm32-timer-trigger drivers.
> + *
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/iio/iio.h>
> +#include <linux/mfd/stm32-lptimer.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +
> +struct stm32_lptim_cnt {
> +	struct device *dev;
> +	struct regmap *regmap;
> +	struct clk *clk;
> +	u32 preset;
> +	u32 polarity;
> +	u32 quadrature_mode;
> +};
> +
> +static int stm32_lptim_is_enabled(struct stm32_lptim_cnt *priv)
> +{
> +	u32 val;
> +	int ret;
> +
> +	ret = regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
> +	if (ret)
> +		return ret;
> +
> +	return FIELD_GET(STM32_LPTIM_ENABLE, val);
> +}
> +
> +static int stm32_lptim_set_enable_state(struct stm32_lptim_cnt *priv,
> +					int enable)
> +{
> +	int ret;
> +	u32 val;
> +
> +	val = FIELD_PREP(STM32_LPTIM_ENABLE, enable);
> +	ret = regmap_write(priv->regmap, STM32_LPTIM_CR, val);
> +	if (ret)
> +		return ret;
> +
> +	if (!enable) {
> +		clk_disable(priv->clk);
> +		return 0;
> +	}
> +
> +	/* LP timer must be enabled before writing CMP & ARR */
> +	ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, priv->preset);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, 0);
> +	if (ret)
> +		return ret;
> +
> +	/* ensure CMP & ARR registers are properly written */
> +	ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val,
> +				       (val & STM32_LPTIM_CMPOK_ARROK),
> +				       100, 1000);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,
> +			   STM32_LPTIM_CMPOKCF_ARROKCF);
> +	if (ret)
> +		return ret;
> +
> +	ret = clk_enable(priv->clk);
> +	if (ret) {
> +		regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
> +		return ret;
> +	}
> +
> +	/* Start LP timer in continuous mode */
> +	return regmap_update_bits(priv->regmap, STM32_LPTIM_CR,
> +				  STM32_LPTIM_CNTSTRT, STM32_LPTIM_CNTSTRT);
> +}
> +
> +static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
> +{
> +	u32 mask = STM32_LPTIM_ENC | STM32_LPTIM_COUNTMODE |
> +		   STM32_LPTIM_CKPOL | STM32_LPTIM_PRESC;
> +	u32 val;
> +
> +	/* Setup LP timer encoder/counter and polarity, without prescaler */
> +	if (priv->quadrature_mode)
> +		val = enable ? STM32_LPTIM_ENC : 0;
> +	else
> +		val = enable ? STM32_LPTIM_COUNTMODE : 0;
> +	val |= FIELD_PREP(STM32_LPTIM_CKPOL, enable ? priv->polarity : 0);
> +
> +	return regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask, val);
> +}
> +
> +static int stm32_lptim_write_raw(struct iio_dev *indio_dev,
> +				 struct iio_chan_spec const *chan,
> +				 int val, int val2, long mask)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_ENABLE:
> +		if (val < 0 || val > 1)
> +			return -EINVAL;
> +
> +		/* Check nobody uses the timer, or already disabled/enabled */
> +		ret = stm32_lptim_is_enabled(priv);
> +		if ((ret < 0) || (!ret && !val))
> +			return ret;
> +		if (val && ret)
> +			return -EBUSY;
> +
> +		ret = stm32_lptim_setup(priv, val);
> +		if (ret)
> +			return ret;
> +		return stm32_lptim_set_enable_state(priv, val);
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int stm32_lptim_read_raw(struct iio_dev *indio_dev,
> +				struct iio_chan_spec const *chan,
> +				int *val, int *val2, long mask)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +	u32 dat;
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = regmap_read(priv->regmap, STM32_LPTIM_CNT, &dat);
> +		if (ret)
> +			return ret;
> +		*val = dat;
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_ENABLE:
> +		ret = stm32_lptim_is_enabled(priv);
> +		if (ret < 0)
> +			return ret;
> +		*val = ret;
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_SCALE:
> +		/* Non-quadrature mode: scale = 1 */
> +		*val = 1;
> +		*val2 = 0;
> +		if (priv->quadrature_mode) {
> +			/*
> +			 * Quadrature encoder mode:
> +			 * - both edges, quarter cycle, scale is 0.25
> +			 * - either rising/falling edge scale is 0.5
> +			 */
> +			if (priv->polarity > 1)
> +				*val2 = 2;
> +			else
> +				*val2 = 1;
> +		}
> +		return IIO_VAL_FRACTIONAL_LOG2;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static const struct iio_info stm32_lptim_cnt_iio_info = {
> +	.read_raw = stm32_lptim_read_raw,
> +	.write_raw = stm32_lptim_write_raw,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static const char *const stm32_lptim_quadrature_modes[] = {
> +	"non-quadrature",
> +	"quadrature",
> +};
> +
> +static int stm32_lptim_get_quadrature_mode(struct iio_dev *indio_dev,
> +					   const struct iio_chan_spec *chan)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +
> +	return priv->quadrature_mode;
> +}
> +
> +static int stm32_lptim_set_quadrature_mode(struct iio_dev *indio_dev,
> +					   const struct iio_chan_spec *chan,
> +					   unsigned int type)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +
> +	if (stm32_lptim_is_enabled(priv))
> +		return -EBUSY;
> +
> +	priv->quadrature_mode = type;
> +
> +	return 0;
> +}
> +
> +static const struct iio_enum stm32_lptim_quadrature_mode_en = {
> +	.items = stm32_lptim_quadrature_modes,
> +	.num_items = ARRAY_SIZE(stm32_lptim_quadrature_modes),
> +	.get = stm32_lptim_get_quadrature_mode,
> +	.set = stm32_lptim_set_quadrature_mode,
> +};
> +
> +static const char * const stm32_lptim_cnt_polarity[] = {
> +	"rising-edge", "falling-edge", "both-edges",
> +};
> +
> +static int stm32_lptim_cnt_get_polarity(struct iio_dev *indio_dev,
> +					const struct iio_chan_spec *chan)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +
> +	return priv->polarity;
> +}
> +
> +static int stm32_lptim_cnt_set_polarity(struct iio_dev *indio_dev,
> +					const struct iio_chan_spec *chan,
> +					unsigned int type)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +
> +	if (stm32_lptim_is_enabled(priv))
> +		return -EBUSY;
> +
> +	priv->polarity = type;
> +
> +	return 0;
> +}
> +
> +static const struct iio_enum stm32_lptim_cnt_polarity_en = {
> +	.items = stm32_lptim_cnt_polarity,
> +	.num_items = ARRAY_SIZE(stm32_lptim_cnt_polarity),
> +	.get = stm32_lptim_cnt_get_polarity,
> +	.set = stm32_lptim_cnt_set_polarity,
> +};
> +
> +static ssize_t stm32_lptim_cnt_get_preset(struct iio_dev *indio_dev,
> +					  uintptr_t private,
> +					  const struct iio_chan_spec *chan,
> +					  char *buf)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset);
> +}
> +
> +static ssize_t stm32_lptim_cnt_set_preset(struct iio_dev *indio_dev,
> +					  uintptr_t private,
> +					  const struct iio_chan_spec *chan,
> +					  const char *buf, size_t len)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +	int ret;
> +
> +	if (stm32_lptim_is_enabled(priv))
> +		return -EBUSY;
> +
> +	ret = kstrtouint(buf, 0, &priv->preset);
> +	if (ret)
> +		return ret;
> +
> +	if (priv->preset > STM32_LPTIM_MAX_ARR)
> +		return -EINVAL;
> +
> +	return len;
> +}
> +
> +/* LP timer with encoder */
> +static const struct iio_chan_spec_ext_info stm32_lptim_enc_ext_info[] = {
> +	{
> +		.name = "preset",
> +		.shared = IIO_SEPARATE,
> +		.read = stm32_lptim_cnt_get_preset,
> +		.write = stm32_lptim_cnt_set_preset,
> +	},
> +	IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
> +	IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
> +	IIO_ENUM("quadrature_mode", IIO_SEPARATE,
> +		 &stm32_lptim_quadrature_mode_en),
> +	IIO_ENUM_AVAILABLE("quadrature_mode", &stm32_lptim_quadrature_mode_en),
> +	{}
> +};
> +
> +static const struct iio_chan_spec stm32_lptim_enc_channels = {
> +	.type = IIO_COUNT,
> +	.channel = 0,
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +			      BIT(IIO_CHAN_INFO_ENABLE) |
> +			      BIT(IIO_CHAN_INFO_SCALE),
> +	.ext_info = stm32_lptim_enc_ext_info,
> +	.indexed = 1,
> +};
> +
> +/* LP timer without encoder (counter only) */
> +static const struct iio_chan_spec_ext_info stm32_lptim_cnt_ext_info[] = {
> +	{
> +		.name = "preset",
> +		.shared = IIO_SEPARATE,
> +		.read = stm32_lptim_cnt_get_preset,
> +		.write = stm32_lptim_cnt_set_preset,
> +	},
> +	IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
> +	IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
> +	{}
> +};
> +
> +static const struct iio_chan_spec stm32_lptim_cnt_channels = {
> +	.type = IIO_COUNT,
> +	.channel = 0,
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +			      BIT(IIO_CHAN_INFO_ENABLE) |
> +			      BIT(IIO_CHAN_INFO_SCALE),
> +	.ext_info = stm32_lptim_cnt_ext_info,
> +	.indexed = 1,
> +};
> +
> +static int stm32_lptim_cnt_probe(struct platform_device *pdev)
> +{
> +	struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
> +	struct stm32_lptim_cnt *priv;
> +	struct iio_dev *indio_dev;
> +
> +	if (IS_ERR_OR_NULL(ddata))
> +		return -EINVAL;
> +
> +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	priv = iio_priv(indio_dev);
> +	priv->dev = &pdev->dev;
> +	priv->regmap = ddata->regmap;
> +	priv->clk = ddata->clk;
> +	priv->preset = STM32_LPTIM_MAX_ARR;
> +
> +	indio_dev->name = dev_name(&pdev->dev);
> +	indio_dev->dev.parent = &pdev->dev;
> +	indio_dev->dev.of_node = pdev->dev.of_node;
> +	indio_dev->info = &stm32_lptim_cnt_iio_info;
> +	if (ddata->has_encoder)
> +		indio_dev->channels = &stm32_lptim_enc_channels;
> +	else
> +		indio_dev->channels = &stm32_lptim_cnt_channels;
> +	indio_dev->num_channels = 1;
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	return devm_iio_device_register(&pdev->dev, indio_dev);
> +}
> +
> +static const struct of_device_id stm32_lptim_cnt_of_match[] = {
> +	{ .compatible = "st,stm32-lptimer-counter", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, stm32_lptim_cnt_of_match);
> +
> +static struct platform_driver stm32_lptim_cnt_driver = {
> +	.probe = stm32_lptim_cnt_probe,
> +	.driver = {
> +		.name = "stm32-lptimer-counter",
> +		.of_match_table = stm32_lptim_cnt_of_match,
> +	},
> +};
> +module_platform_driver(stm32_lptim_cnt_driver);
> +
> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
> +MODULE_ALIAS("platform:stm32-lptimer-counter");
> +MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
> +MODULE_LICENSE("GPL v2");

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 8/9] iio: counter: Add support for STM32 LPTimer
@ 2017-07-09 20:12     ` Jonathan Cameron
  0 siblings, 0 replies; 63+ messages in thread
From: Jonathan Cameron @ 2017-07-09 20:12 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: lee.jones, benjamin.gaignard, thierry.reding, robh+dt,
	mark.rutland, alexandre.torgue, mcoquelin.stm32,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm, William Breathitt Gray

On Fri, 7 Jul 2017 18:31:07 +0200
Fabrice Gasnier <fabrice.gasnier@st.com> wrote:

> 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>
So the obvious question on this one is whether to to delay merging
until we have come to some decision on William's proposed new interface.

My gut feeling is no - we are supporting this interface anyway going
forward for a couple of devices.  Even if the new interface comes
along reasonably quickly I don't have any problem supporting this
approach for one more driver.

So I'd like to let this sit for a little while longer ideally for other
opinions.

From me however it is good to go.

Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> ---
> Changes in v2:
> - s/Low Power/Low-Power
> - update few comments
> ---
>  .../ABI/testing/sysfs-bus-iio-lptimer-stm32        |  57 +++
>  drivers/iio/counter/Kconfig                        |   9 +
>  drivers/iio/counter/Makefile                       |   1 +
>  drivers/iio/counter/stm32-lptimer-cnt.c            | 383 +++++++++++++++++++++
>  4 files changed, 450 insertions(+)
>  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
>  create mode 100644 drivers/iio/counter/stm32-lptimer-cnt.c
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32 b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
> new file mode 100644
> index 0000000..ad2cc63
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
> @@ -0,0 +1,57 @@
> +What:		/sys/bus/iio/devices/iio:deviceX/in_count0_preset
> +KernelVersion:	4.13
> +Contact:	fabrice.gasnier@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
So this is our one oddity.  I'm happy enough with the interface until if
we come up with something better.  So lets go with this for now.
> +
> +		In non-quadrature mode, device counts up on active edge.
> +		In quadrature mode, encoder counting scenarios are as follows:
> +		----------------------------------------------------------------
> +		| Active  | Level on |      IN1 signal    |     IN2 signal     |
> +		| edge    | opposite |------------------------------------------
> +		|         | signal   |  Rising  | Falling |  Rising  | Falling |
> +		----------------------------------------------------------------
> +		| Rising  | High ->  |   Down   |    -    |    Up    |    -    |
> +		| edge    | Low  ->  |    Up    |    -    |   Down   |    -    |
> +		----------------------------------------------------------------
> +		| Falling | High ->  |    -     |    Up   |    -     |   Down  |
> +		| edge    | Low  ->  |    -     |   Down  |    -     |    Up   |
> +		----------------------------------------------------------------
> +		| Both    | High ->  |   Down   |    Up   |    Up    |   Down  |
> +		| edges   | Low  ->  |    Up    |   Down  |   Down   |    Up   |
> +		----------------------------------------------------------------
> diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
> index b37e5fc..474e1ac 100644
> --- a/drivers/iio/counter/Kconfig
> +++ b/drivers/iio/counter/Kconfig
> @@ -21,4 +21,13 @@ config 104_QUAD_8
>  	  The base port addresses for the devices may be configured via the base
>  	  array module parameter.
>  
> +config STM32_LPTIMER_CNT
> +	tristate "STM32 LP Timer encoder counter driver"
> +	depends on MFD_STM32_LPTIMER || COMPILE_TEST
> +	help
> +	  Select this option to enable STM32 Low-Power Timer quadrature encoder
> +	  and counter driver.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called stm32-lptimer-cnt.
>  endmenu
> diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
> index 007e884..1b9a896 100644
> --- a/drivers/iio/counter/Makefile
> +++ b/drivers/iio/counter/Makefile
> @@ -5,3 +5,4 @@
>  # When adding new entries keep the list in alphabetical order
>  
>  obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
> +obj-$(CONFIG_STM32_LPTIMER_CNT)	+= stm32-lptimer-cnt.o
> diff --git a/drivers/iio/counter/stm32-lptimer-cnt.c b/drivers/iio/counter/stm32-lptimer-cnt.c
> new file mode 100644
> index 0000000..1c5909b
> --- /dev/null
> +++ b/drivers/iio/counter/stm32-lptimer-cnt.c
> @@ -0,0 +1,383 @@
> +/*
> + * STM32 Low-Power Timer Encoder and Counter driver
> + *
> + * Copyright (C) STMicroelectronics 2017
> + *
> + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>
> + *
> + * Inspired by 104-quad-8 and stm32-timer-trigger drivers.
> + *
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/iio/iio.h>
> +#include <linux/mfd/stm32-lptimer.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +
> +struct stm32_lptim_cnt {
> +	struct device *dev;
> +	struct regmap *regmap;
> +	struct clk *clk;
> +	u32 preset;
> +	u32 polarity;
> +	u32 quadrature_mode;
> +};
> +
> +static int stm32_lptim_is_enabled(struct stm32_lptim_cnt *priv)
> +{
> +	u32 val;
> +	int ret;
> +
> +	ret = regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
> +	if (ret)
> +		return ret;
> +
> +	return FIELD_GET(STM32_LPTIM_ENABLE, val);
> +}
> +
> +static int stm32_lptim_set_enable_state(struct stm32_lptim_cnt *priv,
> +					int enable)
> +{
> +	int ret;
> +	u32 val;
> +
> +	val = FIELD_PREP(STM32_LPTIM_ENABLE, enable);
> +	ret = regmap_write(priv->regmap, STM32_LPTIM_CR, val);
> +	if (ret)
> +		return ret;
> +
> +	if (!enable) {
> +		clk_disable(priv->clk);
> +		return 0;
> +	}
> +
> +	/* LP timer must be enabled before writing CMP & ARR */
> +	ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, priv->preset);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, 0);
> +	if (ret)
> +		return ret;
> +
> +	/* ensure CMP & ARR registers are properly written */
> +	ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val,
> +				       (val & STM32_LPTIM_CMPOK_ARROK),
> +				       100, 1000);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,
> +			   STM32_LPTIM_CMPOKCF_ARROKCF);
> +	if (ret)
> +		return ret;
> +
> +	ret = clk_enable(priv->clk);
> +	if (ret) {
> +		regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
> +		return ret;
> +	}
> +
> +	/* Start LP timer in continuous mode */
> +	return regmap_update_bits(priv->regmap, STM32_LPTIM_CR,
> +				  STM32_LPTIM_CNTSTRT, STM32_LPTIM_CNTSTRT);
> +}
> +
> +static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
> +{
> +	u32 mask = STM32_LPTIM_ENC | STM32_LPTIM_COUNTMODE |
> +		   STM32_LPTIM_CKPOL | STM32_LPTIM_PRESC;
> +	u32 val;
> +
> +	/* Setup LP timer encoder/counter and polarity, without prescaler */
> +	if (priv->quadrature_mode)
> +		val = enable ? STM32_LPTIM_ENC : 0;
> +	else
> +		val = enable ? STM32_LPTIM_COUNTMODE : 0;
> +	val |= FIELD_PREP(STM32_LPTIM_CKPOL, enable ? priv->polarity : 0);
> +
> +	return regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask, val);
> +}
> +
> +static int stm32_lptim_write_raw(struct iio_dev *indio_dev,
> +				 struct iio_chan_spec const *chan,
> +				 int val, int val2, long mask)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_ENABLE:
> +		if (val < 0 || val > 1)
> +			return -EINVAL;
> +
> +		/* Check nobody uses the timer, or already disabled/enabled */
> +		ret = stm32_lptim_is_enabled(priv);
> +		if ((ret < 0) || (!ret && !val))
> +			return ret;
> +		if (val && ret)
> +			return -EBUSY;
> +
> +		ret = stm32_lptim_setup(priv, val);
> +		if (ret)
> +			return ret;
> +		return stm32_lptim_set_enable_state(priv, val);
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int stm32_lptim_read_raw(struct iio_dev *indio_dev,
> +				struct iio_chan_spec const *chan,
> +				int *val, int *val2, long mask)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +	u32 dat;
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = regmap_read(priv->regmap, STM32_LPTIM_CNT, &dat);
> +		if (ret)
> +			return ret;
> +		*val = dat;
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_ENABLE:
> +		ret = stm32_lptim_is_enabled(priv);
> +		if (ret < 0)
> +			return ret;
> +		*val = ret;
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_SCALE:
> +		/* Non-quadrature mode: scale = 1 */
> +		*val = 1;
> +		*val2 = 0;
> +		if (priv->quadrature_mode) {
> +			/*
> +			 * Quadrature encoder mode:
> +			 * - both edges, quarter cycle, scale is 0.25
> +			 * - either rising/falling edge scale is 0.5
> +			 */
> +			if (priv->polarity > 1)
> +				*val2 = 2;
> +			else
> +				*val2 = 1;
> +		}
> +		return IIO_VAL_FRACTIONAL_LOG2;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static const struct iio_info stm32_lptim_cnt_iio_info = {
> +	.read_raw = stm32_lptim_read_raw,
> +	.write_raw = stm32_lptim_write_raw,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static const char *const stm32_lptim_quadrature_modes[] = {
> +	"non-quadrature",
> +	"quadrature",
> +};
> +
> +static int stm32_lptim_get_quadrature_mode(struct iio_dev *indio_dev,
> +					   const struct iio_chan_spec *chan)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +
> +	return priv->quadrature_mode;
> +}
> +
> +static int stm32_lptim_set_quadrature_mode(struct iio_dev *indio_dev,
> +					   const struct iio_chan_spec *chan,
> +					   unsigned int type)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +
> +	if (stm32_lptim_is_enabled(priv))
> +		return -EBUSY;
> +
> +	priv->quadrature_mode = type;
> +
> +	return 0;
> +}
> +
> +static const struct iio_enum stm32_lptim_quadrature_mode_en = {
> +	.items = stm32_lptim_quadrature_modes,
> +	.num_items = ARRAY_SIZE(stm32_lptim_quadrature_modes),
> +	.get = stm32_lptim_get_quadrature_mode,
> +	.set = stm32_lptim_set_quadrature_mode,
> +};
> +
> +static const char * const stm32_lptim_cnt_polarity[] = {
> +	"rising-edge", "falling-edge", "both-edges",
> +};
> +
> +static int stm32_lptim_cnt_get_polarity(struct iio_dev *indio_dev,
> +					const struct iio_chan_spec *chan)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +
> +	return priv->polarity;
> +}
> +
> +static int stm32_lptim_cnt_set_polarity(struct iio_dev *indio_dev,
> +					const struct iio_chan_spec *chan,
> +					unsigned int type)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +
> +	if (stm32_lptim_is_enabled(priv))
> +		return -EBUSY;
> +
> +	priv->polarity = type;
> +
> +	return 0;
> +}
> +
> +static const struct iio_enum stm32_lptim_cnt_polarity_en = {
> +	.items = stm32_lptim_cnt_polarity,
> +	.num_items = ARRAY_SIZE(stm32_lptim_cnt_polarity),
> +	.get = stm32_lptim_cnt_get_polarity,
> +	.set = stm32_lptim_cnt_set_polarity,
> +};
> +
> +static ssize_t stm32_lptim_cnt_get_preset(struct iio_dev *indio_dev,
> +					  uintptr_t private,
> +					  const struct iio_chan_spec *chan,
> +					  char *buf)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset);
> +}
> +
> +static ssize_t stm32_lptim_cnt_set_preset(struct iio_dev *indio_dev,
> +					  uintptr_t private,
> +					  const struct iio_chan_spec *chan,
> +					  const char *buf, size_t len)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +	int ret;
> +
> +	if (stm32_lptim_is_enabled(priv))
> +		return -EBUSY;
> +
> +	ret = kstrtouint(buf, 0, &priv->preset);
> +	if (ret)
> +		return ret;
> +
> +	if (priv->preset > STM32_LPTIM_MAX_ARR)
> +		return -EINVAL;
> +
> +	return len;
> +}
> +
> +/* LP timer with encoder */
> +static const struct iio_chan_spec_ext_info stm32_lptim_enc_ext_info[] = {
> +	{
> +		.name = "preset",
> +		.shared = IIO_SEPARATE,
> +		.read = stm32_lptim_cnt_get_preset,
> +		.write = stm32_lptim_cnt_set_preset,
> +	},
> +	IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
> +	IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
> +	IIO_ENUM("quadrature_mode", IIO_SEPARATE,
> +		 &stm32_lptim_quadrature_mode_en),
> +	IIO_ENUM_AVAILABLE("quadrature_mode", &stm32_lptim_quadrature_mode_en),
> +	{}
> +};
> +
> +static const struct iio_chan_spec stm32_lptim_enc_channels = {
> +	.type = IIO_COUNT,
> +	.channel = 0,
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +			      BIT(IIO_CHAN_INFO_ENABLE) |
> +			      BIT(IIO_CHAN_INFO_SCALE),
> +	.ext_info = stm32_lptim_enc_ext_info,
> +	.indexed = 1,
> +};
> +
> +/* LP timer without encoder (counter only) */
> +static const struct iio_chan_spec_ext_info stm32_lptim_cnt_ext_info[] = {
> +	{
> +		.name = "preset",
> +		.shared = IIO_SEPARATE,
> +		.read = stm32_lptim_cnt_get_preset,
> +		.write = stm32_lptim_cnt_set_preset,
> +	},
> +	IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
> +	IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
> +	{}
> +};
> +
> +static const struct iio_chan_spec stm32_lptim_cnt_channels = {
> +	.type = IIO_COUNT,
> +	.channel = 0,
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +			      BIT(IIO_CHAN_INFO_ENABLE) |
> +			      BIT(IIO_CHAN_INFO_SCALE),
> +	.ext_info = stm32_lptim_cnt_ext_info,
> +	.indexed = 1,
> +};
> +
> +static int stm32_lptim_cnt_probe(struct platform_device *pdev)
> +{
> +	struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
> +	struct stm32_lptim_cnt *priv;
> +	struct iio_dev *indio_dev;
> +
> +	if (IS_ERR_OR_NULL(ddata))
> +		return -EINVAL;
> +
> +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	priv = iio_priv(indio_dev);
> +	priv->dev = &pdev->dev;
> +	priv->regmap = ddata->regmap;
> +	priv->clk = ddata->clk;
> +	priv->preset = STM32_LPTIM_MAX_ARR;
> +
> +	indio_dev->name = dev_name(&pdev->dev);
> +	indio_dev->dev.parent = &pdev->dev;
> +	indio_dev->dev.of_node = pdev->dev.of_node;
> +	indio_dev->info = &stm32_lptim_cnt_iio_info;
> +	if (ddata->has_encoder)
> +		indio_dev->channels = &stm32_lptim_enc_channels;
> +	else
> +		indio_dev->channels = &stm32_lptim_cnt_channels;
> +	indio_dev->num_channels = 1;
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	return devm_iio_device_register(&pdev->dev, indio_dev);
> +}
> +
> +static const struct of_device_id stm32_lptim_cnt_of_match[] = {
> +	{ .compatible = "st,stm32-lptimer-counter", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, stm32_lptim_cnt_of_match);
> +
> +static struct platform_driver stm32_lptim_cnt_driver = {
> +	.probe = stm32_lptim_cnt_probe,
> +	.driver = {
> +		.name = "stm32-lptimer-counter",
> +		.of_match_table = stm32_lptim_cnt_of_match,
> +	},
> +};
> +module_platform_driver(stm32_lptim_cnt_driver);
> +
> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
> +MODULE_ALIAS("platform:stm32-lptimer-counter");
> +MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
> +MODULE_LICENSE("GPL v2");

^ permalink raw reply	[flat|nested] 63+ messages in thread

* [PATCH v3 8/9] iio: counter: Add support for STM32 LPTimer
@ 2017-07-09 20:12     ` Jonathan Cameron
  0 siblings, 0 replies; 63+ messages in thread
From: Jonathan Cameron @ 2017-07-09 20:12 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, 7 Jul 2017 18:31:07 +0200
Fabrice Gasnier <fabrice.gasnier@st.com> wrote:

> 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>
So the obvious question on this one is whether to to delay merging
until we have come to some decision on William's proposed new interface.

My gut feeling is no - we are supporting this interface anyway going
forward for a couple of devices.  Even if the new interface comes
along reasonably quickly I don't have any problem supporting this
approach for one more driver.

So I'd like to let this sit for a little while longer ideally for other
opinions.

>From me however it is good to go.

Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> ---
> Changes in v2:
> - s/Low Power/Low-Power
> - update few comments
> ---
>  .../ABI/testing/sysfs-bus-iio-lptimer-stm32        |  57 +++
>  drivers/iio/counter/Kconfig                        |   9 +
>  drivers/iio/counter/Makefile                       |   1 +
>  drivers/iio/counter/stm32-lptimer-cnt.c            | 383 +++++++++++++++++++++
>  4 files changed, 450 insertions(+)
>  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
>  create mode 100644 drivers/iio/counter/stm32-lptimer-cnt.c
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32 b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
> new file mode 100644
> index 0000000..ad2cc63
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
> @@ -0,0 +1,57 @@
> +What:		/sys/bus/iio/devices/iio:deviceX/in_count0_preset
> +KernelVersion:	4.13
> +Contact:	fabrice.gasnier at 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 at 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 at 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 at 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 at st.com
> +Description:
> +		Configure the device encoder/counter active edge:
> +		- rising-edge
> +		- falling-edge
> +		- both-edges
So this is our one oddity.  I'm happy enough with the interface until if
we come up with something better.  So lets go with this for now.
> +
> +		In non-quadrature mode, device counts up on active edge.
> +		In quadrature mode, encoder counting scenarios are as follows:
> +		----------------------------------------------------------------
> +		| Active  | Level on |      IN1 signal    |     IN2 signal     |
> +		| edge    | opposite |------------------------------------------
> +		|         | signal   |  Rising  | Falling |  Rising  | Falling |
> +		----------------------------------------------------------------
> +		| Rising  | High ->  |   Down   |    -    |    Up    |    -    |
> +		| edge    | Low  ->  |    Up    |    -    |   Down   |    -    |
> +		----------------------------------------------------------------
> +		| Falling | High ->  |    -     |    Up   |    -     |   Down  |
> +		| edge    | Low  ->  |    -     |   Down  |    -     |    Up   |
> +		----------------------------------------------------------------
> +		| Both    | High ->  |   Down   |    Up   |    Up    |   Down  |
> +		| edges   | Low  ->  |    Up    |   Down  |   Down   |    Up   |
> +		----------------------------------------------------------------
> diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
> index b37e5fc..474e1ac 100644
> --- a/drivers/iio/counter/Kconfig
> +++ b/drivers/iio/counter/Kconfig
> @@ -21,4 +21,13 @@ config 104_QUAD_8
>  	  The base port addresses for the devices may be configured via the base
>  	  array module parameter.
>  
> +config STM32_LPTIMER_CNT
> +	tristate "STM32 LP Timer encoder counter driver"
> +	depends on MFD_STM32_LPTIMER || COMPILE_TEST
> +	help
> +	  Select this option to enable STM32 Low-Power Timer quadrature encoder
> +	  and counter driver.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called stm32-lptimer-cnt.
>  endmenu
> diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
> index 007e884..1b9a896 100644
> --- a/drivers/iio/counter/Makefile
> +++ b/drivers/iio/counter/Makefile
> @@ -5,3 +5,4 @@
>  # When adding new entries keep the list in alphabetical order
>  
>  obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
> +obj-$(CONFIG_STM32_LPTIMER_CNT)	+= stm32-lptimer-cnt.o
> diff --git a/drivers/iio/counter/stm32-lptimer-cnt.c b/drivers/iio/counter/stm32-lptimer-cnt.c
> new file mode 100644
> index 0000000..1c5909b
> --- /dev/null
> +++ b/drivers/iio/counter/stm32-lptimer-cnt.c
> @@ -0,0 +1,383 @@
> +/*
> + * STM32 Low-Power Timer Encoder and Counter driver
> + *
> + * Copyright (C) STMicroelectronics 2017
> + *
> + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>
> + *
> + * Inspired by 104-quad-8 and stm32-timer-trigger drivers.
> + *
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/iio/iio.h>
> +#include <linux/mfd/stm32-lptimer.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +
> +struct stm32_lptim_cnt {
> +	struct device *dev;
> +	struct regmap *regmap;
> +	struct clk *clk;
> +	u32 preset;
> +	u32 polarity;
> +	u32 quadrature_mode;
> +};
> +
> +static int stm32_lptim_is_enabled(struct stm32_lptim_cnt *priv)
> +{
> +	u32 val;
> +	int ret;
> +
> +	ret = regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
> +	if (ret)
> +		return ret;
> +
> +	return FIELD_GET(STM32_LPTIM_ENABLE, val);
> +}
> +
> +static int stm32_lptim_set_enable_state(struct stm32_lptim_cnt *priv,
> +					int enable)
> +{
> +	int ret;
> +	u32 val;
> +
> +	val = FIELD_PREP(STM32_LPTIM_ENABLE, enable);
> +	ret = regmap_write(priv->regmap, STM32_LPTIM_CR, val);
> +	if (ret)
> +		return ret;
> +
> +	if (!enable) {
> +		clk_disable(priv->clk);
> +		return 0;
> +	}
> +
> +	/* LP timer must be enabled before writing CMP & ARR */
> +	ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, priv->preset);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, 0);
> +	if (ret)
> +		return ret;
> +
> +	/* ensure CMP & ARR registers are properly written */
> +	ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val,
> +				       (val & STM32_LPTIM_CMPOK_ARROK),
> +				       100, 1000);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,
> +			   STM32_LPTIM_CMPOKCF_ARROKCF);
> +	if (ret)
> +		return ret;
> +
> +	ret = clk_enable(priv->clk);
> +	if (ret) {
> +		regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
> +		return ret;
> +	}
> +
> +	/* Start LP timer in continuous mode */
> +	return regmap_update_bits(priv->regmap, STM32_LPTIM_CR,
> +				  STM32_LPTIM_CNTSTRT, STM32_LPTIM_CNTSTRT);
> +}
> +
> +static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
> +{
> +	u32 mask = STM32_LPTIM_ENC | STM32_LPTIM_COUNTMODE |
> +		   STM32_LPTIM_CKPOL | STM32_LPTIM_PRESC;
> +	u32 val;
> +
> +	/* Setup LP timer encoder/counter and polarity, without prescaler */
> +	if (priv->quadrature_mode)
> +		val = enable ? STM32_LPTIM_ENC : 0;
> +	else
> +		val = enable ? STM32_LPTIM_COUNTMODE : 0;
> +	val |= FIELD_PREP(STM32_LPTIM_CKPOL, enable ? priv->polarity : 0);
> +
> +	return regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask, val);
> +}
> +
> +static int stm32_lptim_write_raw(struct iio_dev *indio_dev,
> +				 struct iio_chan_spec const *chan,
> +				 int val, int val2, long mask)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_ENABLE:
> +		if (val < 0 || val > 1)
> +			return -EINVAL;
> +
> +		/* Check nobody uses the timer, or already disabled/enabled */
> +		ret = stm32_lptim_is_enabled(priv);
> +		if ((ret < 0) || (!ret && !val))
> +			return ret;
> +		if (val && ret)
> +			return -EBUSY;
> +
> +		ret = stm32_lptim_setup(priv, val);
> +		if (ret)
> +			return ret;
> +		return stm32_lptim_set_enable_state(priv, val);
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int stm32_lptim_read_raw(struct iio_dev *indio_dev,
> +				struct iio_chan_spec const *chan,
> +				int *val, int *val2, long mask)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +	u32 dat;
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = regmap_read(priv->regmap, STM32_LPTIM_CNT, &dat);
> +		if (ret)
> +			return ret;
> +		*val = dat;
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_ENABLE:
> +		ret = stm32_lptim_is_enabled(priv);
> +		if (ret < 0)
> +			return ret;
> +		*val = ret;
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_SCALE:
> +		/* Non-quadrature mode: scale = 1 */
> +		*val = 1;
> +		*val2 = 0;
> +		if (priv->quadrature_mode) {
> +			/*
> +			 * Quadrature encoder mode:
> +			 * - both edges, quarter cycle, scale is 0.25
> +			 * - either rising/falling edge scale is 0.5
> +			 */
> +			if (priv->polarity > 1)
> +				*val2 = 2;
> +			else
> +				*val2 = 1;
> +		}
> +		return IIO_VAL_FRACTIONAL_LOG2;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static const struct iio_info stm32_lptim_cnt_iio_info = {
> +	.read_raw = stm32_lptim_read_raw,
> +	.write_raw = stm32_lptim_write_raw,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static const char *const stm32_lptim_quadrature_modes[] = {
> +	"non-quadrature",
> +	"quadrature",
> +};
> +
> +static int stm32_lptim_get_quadrature_mode(struct iio_dev *indio_dev,
> +					   const struct iio_chan_spec *chan)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +
> +	return priv->quadrature_mode;
> +}
> +
> +static int stm32_lptim_set_quadrature_mode(struct iio_dev *indio_dev,
> +					   const struct iio_chan_spec *chan,
> +					   unsigned int type)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +
> +	if (stm32_lptim_is_enabled(priv))
> +		return -EBUSY;
> +
> +	priv->quadrature_mode = type;
> +
> +	return 0;
> +}
> +
> +static const struct iio_enum stm32_lptim_quadrature_mode_en = {
> +	.items = stm32_lptim_quadrature_modes,
> +	.num_items = ARRAY_SIZE(stm32_lptim_quadrature_modes),
> +	.get = stm32_lptim_get_quadrature_mode,
> +	.set = stm32_lptim_set_quadrature_mode,
> +};
> +
> +static const char * const stm32_lptim_cnt_polarity[] = {
> +	"rising-edge", "falling-edge", "both-edges",
> +};
> +
> +static int stm32_lptim_cnt_get_polarity(struct iio_dev *indio_dev,
> +					const struct iio_chan_spec *chan)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +
> +	return priv->polarity;
> +}
> +
> +static int stm32_lptim_cnt_set_polarity(struct iio_dev *indio_dev,
> +					const struct iio_chan_spec *chan,
> +					unsigned int type)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +
> +	if (stm32_lptim_is_enabled(priv))
> +		return -EBUSY;
> +
> +	priv->polarity = type;
> +
> +	return 0;
> +}
> +
> +static const struct iio_enum stm32_lptim_cnt_polarity_en = {
> +	.items = stm32_lptim_cnt_polarity,
> +	.num_items = ARRAY_SIZE(stm32_lptim_cnt_polarity),
> +	.get = stm32_lptim_cnt_get_polarity,
> +	.set = stm32_lptim_cnt_set_polarity,
> +};
> +
> +static ssize_t stm32_lptim_cnt_get_preset(struct iio_dev *indio_dev,
> +					  uintptr_t private,
> +					  const struct iio_chan_spec *chan,
> +					  char *buf)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset);
> +}
> +
> +static ssize_t stm32_lptim_cnt_set_preset(struct iio_dev *indio_dev,
> +					  uintptr_t private,
> +					  const struct iio_chan_spec *chan,
> +					  const char *buf, size_t len)
> +{
> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
> +	int ret;
> +
> +	if (stm32_lptim_is_enabled(priv))
> +		return -EBUSY;
> +
> +	ret = kstrtouint(buf, 0, &priv->preset);
> +	if (ret)
> +		return ret;
> +
> +	if (priv->preset > STM32_LPTIM_MAX_ARR)
> +		return -EINVAL;
> +
> +	return len;
> +}
> +
> +/* LP timer with encoder */
> +static const struct iio_chan_spec_ext_info stm32_lptim_enc_ext_info[] = {
> +	{
> +		.name = "preset",
> +		.shared = IIO_SEPARATE,
> +		.read = stm32_lptim_cnt_get_preset,
> +		.write = stm32_lptim_cnt_set_preset,
> +	},
> +	IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
> +	IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
> +	IIO_ENUM("quadrature_mode", IIO_SEPARATE,
> +		 &stm32_lptim_quadrature_mode_en),
> +	IIO_ENUM_AVAILABLE("quadrature_mode", &stm32_lptim_quadrature_mode_en),
> +	{}
> +};
> +
> +static const struct iio_chan_spec stm32_lptim_enc_channels = {
> +	.type = IIO_COUNT,
> +	.channel = 0,
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +			      BIT(IIO_CHAN_INFO_ENABLE) |
> +			      BIT(IIO_CHAN_INFO_SCALE),
> +	.ext_info = stm32_lptim_enc_ext_info,
> +	.indexed = 1,
> +};
> +
> +/* LP timer without encoder (counter only) */
> +static const struct iio_chan_spec_ext_info stm32_lptim_cnt_ext_info[] = {
> +	{
> +		.name = "preset",
> +		.shared = IIO_SEPARATE,
> +		.read = stm32_lptim_cnt_get_preset,
> +		.write = stm32_lptim_cnt_set_preset,
> +	},
> +	IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
> +	IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
> +	{}
> +};
> +
> +static const struct iio_chan_spec stm32_lptim_cnt_channels = {
> +	.type = IIO_COUNT,
> +	.channel = 0,
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +			      BIT(IIO_CHAN_INFO_ENABLE) |
> +			      BIT(IIO_CHAN_INFO_SCALE),
> +	.ext_info = stm32_lptim_cnt_ext_info,
> +	.indexed = 1,
> +};
> +
> +static int stm32_lptim_cnt_probe(struct platform_device *pdev)
> +{
> +	struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
> +	struct stm32_lptim_cnt *priv;
> +	struct iio_dev *indio_dev;
> +
> +	if (IS_ERR_OR_NULL(ddata))
> +		return -EINVAL;
> +
> +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	priv = iio_priv(indio_dev);
> +	priv->dev = &pdev->dev;
> +	priv->regmap = ddata->regmap;
> +	priv->clk = ddata->clk;
> +	priv->preset = STM32_LPTIM_MAX_ARR;
> +
> +	indio_dev->name = dev_name(&pdev->dev);
> +	indio_dev->dev.parent = &pdev->dev;
> +	indio_dev->dev.of_node = pdev->dev.of_node;
> +	indio_dev->info = &stm32_lptim_cnt_iio_info;
> +	if (ddata->has_encoder)
> +		indio_dev->channels = &stm32_lptim_enc_channels;
> +	else
> +		indio_dev->channels = &stm32_lptim_cnt_channels;
> +	indio_dev->num_channels = 1;
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	return devm_iio_device_register(&pdev->dev, indio_dev);
> +}
> +
> +static const struct of_device_id stm32_lptim_cnt_of_match[] = {
> +	{ .compatible = "st,stm32-lptimer-counter", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, stm32_lptim_cnt_of_match);
> +
> +static struct platform_driver stm32_lptim_cnt_driver = {
> +	.probe = stm32_lptim_cnt_probe,
> +	.driver = {
> +		.name = "stm32-lptimer-counter",
> +		.of_match_table = stm32_lptim_cnt_of_match,
> +	},
> +};
> +module_platform_driver(stm32_lptim_cnt_driver);
> +
> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
> +MODULE_ALIAS("platform:stm32-lptimer-counter");
> +MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
> +MODULE_LICENSE("GPL v2");

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 9/9] iio: adc: stm32: add support for lptimer triggers
@ 2017-07-09 20:13     ` Jonathan Cameron
  0 siblings, 0 replies; 63+ messages in thread
From: Jonathan Cameron @ 2017-07-09 20:13 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: lee.jones, benjamin.gaignard, thierry.reding, robh+dt,
	mark.rutland, alexandre.torgue, mcoquelin.stm32,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm

On Fri, 7 Jul 2017 18:31:08 +0200
Fabrice Gasnier <fabrice.gasnier@st.com> wrote:

> STM32 ADC supports hardware triggers like STM32 Low-Power Timer.
> For instance, STM32H7 ADC may be triggered by instances 1, 2 or 3.
> Add hardware triggered mode so Low-Power Timer Trigger driver can
> validate device.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Acked-by: Jonathan Cameron <jic23@kernel.org>
> ---
>  drivers/iio/adc/stm32-adc.c | 14 ++++++++++++--
>  1 file changed, 12 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> index 5bfcc1f..1147d7e 100644
> --- a/drivers/iio/adc/stm32-adc.c
> +++ b/drivers/iio/adc/stm32-adc.c
> @@ -25,6 +25,7 @@
>  #include <linux/dmaengine.h>
>  #include <linux/iio/iio.h>
>  #include <linux/iio/buffer.h>
> +#include <linux/iio/timer/stm32-lptim-trigger.h>
>  #include <linux/iio/timer/stm32-timer-trigger.h>
>  #include <linux/iio/trigger.h>
>  #include <linux/iio/trigger_consumer.h>
> @@ -182,6 +183,11 @@ enum stm32_adc_extsel {
>  	STM32_EXT13,
>  	STM32_EXT14,
>  	STM32_EXT15,
> +	STM32_EXT16,
> +	STM32_EXT17,
> +	STM32_EXT18,
> +	STM32_EXT19,
> +	STM32_EXT20,
>  };
>  
>  /**
> @@ -480,6 +486,9 @@ struct stm32_adc_info {
>  	{ TIM4_TRGO, STM32_EXT12 },
>  	{ TIM6_TRGO, STM32_EXT13 },
>  	{ TIM3_CH4, STM32_EXT15 },
> +	{ LPTIM1_OUT, STM32_EXT18 },
> +	{ LPTIM2_OUT, STM32_EXT19 },
> +	{ LPTIM3_OUT, STM32_EXT20 },
>  	{},
>  };
>  
> @@ -995,7 +1004,8 @@ static int stm32_adc_get_trig_extsel(struct iio_dev *indio_dev,
>  		 * Checking both stm32 timer trigger type and trig name
>  		 * should be safe against arbitrary trigger names.
>  		 */
> -		if (is_stm32_timer_trigger(trig) &&
> +		if ((is_stm32_timer_trigger(trig) ||
> +		     is_stm32_lptim_trigger(trig)) &&
>  		    !strcmp(adc->cfg->trigs[i].name, trig->name)) {
>  			return adc->cfg->trigs[i].extsel;
>  		}
> @@ -1634,7 +1644,7 @@ static int stm32_adc_probe(struct platform_device *pdev)
>  	indio_dev->dev.parent = &pdev->dev;
>  	indio_dev->dev.of_node = pdev->dev.of_node;
>  	indio_dev->info = &stm32_adc_iio_info;
> -	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->modes = INDIO_DIRECT_MODE | INDIO_HARDWARE_TRIGGERED;
>  
>  	platform_set_drvdata(pdev, adc);
>  

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 9/9] iio: adc: stm32: add support for lptimer triggers
@ 2017-07-09 20:13     ` Jonathan Cameron
  0 siblings, 0 replies; 63+ messages in thread
From: Jonathan Cameron @ 2017-07-09 20:13 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: lee.jones-QSEj5FYQhm4dnm+yROfE0A,
	benjamin.gaignard-QSEj5FYQhm4dnm+yROfE0A,
	thierry.reding-Re5JQEeQqe8AvxtiuMwx3w,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	alexandre.torgue-qxv4g6HH51o,
	mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
	benjamin.gaignard-qxv4g6HH51o, linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-pwm-u79uwXL29TY76Z2rM5mHXA

On Fri, 7 Jul 2017 18:31:08 +0200
Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org> wrote:

> STM32 ADC supports hardware triggers like STM32 Low-Power Timer.
> For instance, STM32H7 ADC may be triggered by instances 1, 2 or 3.
> Add hardware triggered mode so Low-Power Timer Trigger driver can
> validate device.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
Acked-by: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> ---
>  drivers/iio/adc/stm32-adc.c | 14 ++++++++++++--
>  1 file changed, 12 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> index 5bfcc1f..1147d7e 100644
> --- a/drivers/iio/adc/stm32-adc.c
> +++ b/drivers/iio/adc/stm32-adc.c
> @@ -25,6 +25,7 @@
>  #include <linux/dmaengine.h>
>  #include <linux/iio/iio.h>
>  #include <linux/iio/buffer.h>
> +#include <linux/iio/timer/stm32-lptim-trigger.h>
>  #include <linux/iio/timer/stm32-timer-trigger.h>
>  #include <linux/iio/trigger.h>
>  #include <linux/iio/trigger_consumer.h>
> @@ -182,6 +183,11 @@ enum stm32_adc_extsel {
>  	STM32_EXT13,
>  	STM32_EXT14,
>  	STM32_EXT15,
> +	STM32_EXT16,
> +	STM32_EXT17,
> +	STM32_EXT18,
> +	STM32_EXT19,
> +	STM32_EXT20,
>  };
>  
>  /**
> @@ -480,6 +486,9 @@ struct stm32_adc_info {
>  	{ TIM4_TRGO, STM32_EXT12 },
>  	{ TIM6_TRGO, STM32_EXT13 },
>  	{ TIM3_CH4, STM32_EXT15 },
> +	{ LPTIM1_OUT, STM32_EXT18 },
> +	{ LPTIM2_OUT, STM32_EXT19 },
> +	{ LPTIM3_OUT, STM32_EXT20 },
>  	{},
>  };
>  
> @@ -995,7 +1004,8 @@ static int stm32_adc_get_trig_extsel(struct iio_dev *indio_dev,
>  		 * Checking both stm32 timer trigger type and trig name
>  		 * should be safe against arbitrary trigger names.
>  		 */
> -		if (is_stm32_timer_trigger(trig) &&
> +		if ((is_stm32_timer_trigger(trig) ||
> +		     is_stm32_lptim_trigger(trig)) &&
>  		    !strcmp(adc->cfg->trigs[i].name, trig->name)) {
>  			return adc->cfg->trigs[i].extsel;
>  		}
> @@ -1634,7 +1644,7 @@ static int stm32_adc_probe(struct platform_device *pdev)
>  	indio_dev->dev.parent = &pdev->dev;
>  	indio_dev->dev.of_node = pdev->dev.of_node;
>  	indio_dev->info = &stm32_adc_iio_info;
> -	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->modes = INDIO_DIRECT_MODE | INDIO_HARDWARE_TRIGGERED;
>  
>  	platform_set_drvdata(pdev, adc);
>  

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 63+ messages in thread

* [PATCH v3 9/9] iio: adc: stm32: add support for lptimer triggers
@ 2017-07-09 20:13     ` Jonathan Cameron
  0 siblings, 0 replies; 63+ messages in thread
From: Jonathan Cameron @ 2017-07-09 20:13 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, 7 Jul 2017 18:31:08 +0200
Fabrice Gasnier <fabrice.gasnier@st.com> wrote:

> STM32 ADC supports hardware triggers like STM32 Low-Power Timer.
> For instance, STM32H7 ADC may be triggered by instances 1, 2 or 3.
> Add hardware triggered mode so Low-Power Timer Trigger driver can
> validate device.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Acked-by: Jonathan Cameron <jic23@kernel.org>
> ---
>  drivers/iio/adc/stm32-adc.c | 14 ++++++++++++--
>  1 file changed, 12 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> index 5bfcc1f..1147d7e 100644
> --- a/drivers/iio/adc/stm32-adc.c
> +++ b/drivers/iio/adc/stm32-adc.c
> @@ -25,6 +25,7 @@
>  #include <linux/dmaengine.h>
>  #include <linux/iio/iio.h>
>  #include <linux/iio/buffer.h>
> +#include <linux/iio/timer/stm32-lptim-trigger.h>
>  #include <linux/iio/timer/stm32-timer-trigger.h>
>  #include <linux/iio/trigger.h>
>  #include <linux/iio/trigger_consumer.h>
> @@ -182,6 +183,11 @@ enum stm32_adc_extsel {
>  	STM32_EXT13,
>  	STM32_EXT14,
>  	STM32_EXT15,
> +	STM32_EXT16,
> +	STM32_EXT17,
> +	STM32_EXT18,
> +	STM32_EXT19,
> +	STM32_EXT20,
>  };
>  
>  /**
> @@ -480,6 +486,9 @@ struct stm32_adc_info {
>  	{ TIM4_TRGO, STM32_EXT12 },
>  	{ TIM6_TRGO, STM32_EXT13 },
>  	{ TIM3_CH4, STM32_EXT15 },
> +	{ LPTIM1_OUT, STM32_EXT18 },
> +	{ LPTIM2_OUT, STM32_EXT19 },
> +	{ LPTIM3_OUT, STM32_EXT20 },
>  	{},
>  };
>  
> @@ -995,7 +1004,8 @@ static int stm32_adc_get_trig_extsel(struct iio_dev *indio_dev,
>  		 * Checking both stm32 timer trigger type and trig name
>  		 * should be safe against arbitrary trigger names.
>  		 */
> -		if (is_stm32_timer_trigger(trig) &&
> +		if ((is_stm32_timer_trigger(trig) ||
> +		     is_stm32_lptim_trigger(trig)) &&
>  		    !strcmp(adc->cfg->trigs[i].name, trig->name)) {
>  			return adc->cfg->trigs[i].extsel;
>  		}
> @@ -1634,7 +1644,7 @@ static int stm32_adc_probe(struct platform_device *pdev)
>  	indio_dev->dev.parent = &pdev->dev;
>  	indio_dev->dev.of_node = pdev->dev.of_node;
>  	indio_dev->info = &stm32_adc_iio_info;
> -	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->modes = INDIO_DIRECT_MODE | INDIO_HARDWARE_TRIGGERED;
>  
>  	platform_set_drvdata(pdev, adc);
>  

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 8/9] iio: counter: Add support for STM32 LPTimer
  2017-07-09 20:12     ` Jonathan Cameron
  (?)
  (?)
@ 2017-07-10 12:54       ` William Breathitt Gray
  -1 siblings, 0 replies; 63+ messages in thread
From: William Breathitt Gray @ 2017-07-10 12:54 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Fabrice Gasnier, lee.jones, benjamin.gaignard, thierry.reding,
	robh+dt, mark.rutland, alexandre.torgue, mcoquelin.stm32,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm

On Sun, Jul 09, 2017 at 09:12:43PM +0100, Jonathan Cameron wrote:
>On Fri, 7 Jul 2017 18:31:07 +0200
>Fabrice Gasnier <fabrice.gasnier@st.com> wrote:
>
>> 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>
>So the obvious question on this one is whether to to delay merging
>until we have come to some decision on William's proposed new interface.
>
>My gut feeling is no - we are supporting this interface anyway going
>forward for a couple of devices.  Even if the new interface comes
>along reasonably quickly I don't have any problem supporting this
>approach for one more driver.
>
>So I'd like to let this sit for a little while longer ideally for other
>opinions.
>
>From me however it is good to go.
>
>Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

For what it's worth, I don't think we should delay drivers just yet.
Although I plan on releasing the new generic counter and quadrature
counter interfaces in the coming weeks, we're certainly bound to go
through several revisions of them before they are acceptable for merge.

I wouldn't want the development of these interfaces to stall other
drivers, so maintaining the current approach with the anticipation of a
subsequent upgrade patch should be our course of action (at least until
the generic counter interface is solidified and merged).

William Breathitt Gray

>> ---
>> Changes in v2:
>> - s/Low Power/Low-Power
>> - update few comments
>> ---
>>  .../ABI/testing/sysfs-bus-iio-lptimer-stm32        |  57 +++
>>  drivers/iio/counter/Kconfig                        |   9 +
>>  drivers/iio/counter/Makefile                       |   1 +
>>  drivers/iio/counter/stm32-lptimer-cnt.c            | 383 +++++++++++++++++++++
>>  4 files changed, 450 insertions(+)
>>  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
>>  create mode 100644 drivers/iio/counter/stm32-lptimer-cnt.c
>> 
>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32 b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
>> new file mode 100644
>> index 0000000..ad2cc63
>> --- /dev/null
>> +++ b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
>> @@ -0,0 +1,57 @@
>> +What:		/sys/bus/iio/devices/iio:deviceX/in_count0_preset
>> +KernelVersion:	4.13
>> +Contact:	fabrice.gasnier@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
>So this is our one oddity.  I'm happy enough with the interface until if
>we come up with something better.  So lets go with this for now.
>> +
>> +		In non-quadrature mode, device counts up on active edge.
>> +		In quadrature mode, encoder counting scenarios are as follows:
>> +		----------------------------------------------------------------
>> +		| Active  | Level on |      IN1 signal    |     IN2 signal     |
>> +		| edge    | opposite |------------------------------------------
>> +		|         | signal   |  Rising  | Falling |  Rising  | Falling |
>> +		----------------------------------------------------------------
>> +		| Rising  | High ->  |   Down   |    -    |    Up    |    -    |
>> +		| edge    | Low  ->  |    Up    |    -    |   Down   |    -    |
>> +		----------------------------------------------------------------
>> +		| Falling | High ->  |    -     |    Up   |    -     |   Down  |
>> +		| edge    | Low  ->  |    -     |   Down  |    -     |    Up   |
>> +		----------------------------------------------------------------
>> +		| Both    | High ->  |   Down   |    Up   |    Up    |   Down  |
>> +		| edges   | Low  ->  |    Up    |   Down  |   Down   |    Up   |
>> +		----------------------------------------------------------------
>> diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
>> index b37e5fc..474e1ac 100644
>> --- a/drivers/iio/counter/Kconfig
>> +++ b/drivers/iio/counter/Kconfig
>> @@ -21,4 +21,13 @@ config 104_QUAD_8
>>  	  The base port addresses for the devices may be configured via the base
>>  	  array module parameter.
>>  
>> +config STM32_LPTIMER_CNT
>> +	tristate "STM32 LP Timer encoder counter driver"
>> +	depends on MFD_STM32_LPTIMER || COMPILE_TEST
>> +	help
>> +	  Select this option to enable STM32 Low-Power Timer quadrature encoder
>> +	  and counter driver.
>> +
>> +	  To compile this driver as a module, choose M here: the
>> +	  module will be called stm32-lptimer-cnt.
>>  endmenu
>> diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
>> index 007e884..1b9a896 100644
>> --- a/drivers/iio/counter/Makefile
>> +++ b/drivers/iio/counter/Makefile
>> @@ -5,3 +5,4 @@
>>  # When adding new entries keep the list in alphabetical order
>>  
>>  obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
>> +obj-$(CONFIG_STM32_LPTIMER_CNT)	+= stm32-lptimer-cnt.o
>> diff --git a/drivers/iio/counter/stm32-lptimer-cnt.c b/drivers/iio/counter/stm32-lptimer-cnt.c
>> new file mode 100644
>> index 0000000..1c5909b
>> --- /dev/null
>> +++ b/drivers/iio/counter/stm32-lptimer-cnt.c
>> @@ -0,0 +1,383 @@
>> +/*
>> + * STM32 Low-Power Timer Encoder and Counter driver
>> + *
>> + * Copyright (C) STMicroelectronics 2017
>> + *
>> + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>
>> + *
>> + * Inspired by 104-quad-8 and stm32-timer-trigger drivers.
>> + *
>> + * License terms:  GNU General Public License (GPL), version 2
>> + */
>> +
>> +#include <linux/bitfield.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/mfd/stm32-lptimer.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +
>> +struct stm32_lptim_cnt {
>> +	struct device *dev;
>> +	struct regmap *regmap;
>> +	struct clk *clk;
>> +	u32 preset;
>> +	u32 polarity;
>> +	u32 quadrature_mode;
>> +};
>> +
>> +static int stm32_lptim_is_enabled(struct stm32_lptim_cnt *priv)
>> +{
>> +	u32 val;
>> +	int ret;
>> +
>> +	ret = regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
>> +	if (ret)
>> +		return ret;
>> +
>> +	return FIELD_GET(STM32_LPTIM_ENABLE, val);
>> +}
>> +
>> +static int stm32_lptim_set_enable_state(struct stm32_lptim_cnt *priv,
>> +					int enable)
>> +{
>> +	int ret;
>> +	u32 val;
>> +
>> +	val = FIELD_PREP(STM32_LPTIM_ENABLE, enable);
>> +	ret = regmap_write(priv->regmap, STM32_LPTIM_CR, val);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (!enable) {
>> +		clk_disable(priv->clk);
>> +		return 0;
>> +	}
>> +
>> +	/* LP timer must be enabled before writing CMP & ARR */
>> +	ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, priv->preset);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, 0);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* ensure CMP & ARR registers are properly written */
>> +	ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val,
>> +				       (val & STM32_LPTIM_CMPOK_ARROK),
>> +				       100, 1000);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,
>> +			   STM32_LPTIM_CMPOKCF_ARROKCF);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = clk_enable(priv->clk);
>> +	if (ret) {
>> +		regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
>> +		return ret;
>> +	}
>> +
>> +	/* Start LP timer in continuous mode */
>> +	return regmap_update_bits(priv->regmap, STM32_LPTIM_CR,
>> +				  STM32_LPTIM_CNTSTRT, STM32_LPTIM_CNTSTRT);
>> +}
>> +
>> +static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
>> +{
>> +	u32 mask = STM32_LPTIM_ENC | STM32_LPTIM_COUNTMODE |
>> +		   STM32_LPTIM_CKPOL | STM32_LPTIM_PRESC;
>> +	u32 val;
>> +
>> +	/* Setup LP timer encoder/counter and polarity, without prescaler */
>> +	if (priv->quadrature_mode)
>> +		val = enable ? STM32_LPTIM_ENC : 0;
>> +	else
>> +		val = enable ? STM32_LPTIM_COUNTMODE : 0;
>> +	val |= FIELD_PREP(STM32_LPTIM_CKPOL, enable ? priv->polarity : 0);
>> +
>> +	return regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask, val);
>> +}
>> +
>> +static int stm32_lptim_write_raw(struct iio_dev *indio_dev,
>> +				 struct iio_chan_spec const *chan,
>> +				 int val, int val2, long mask)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_ENABLE:
>> +		if (val < 0 || val > 1)
>> +			return -EINVAL;
>> +
>> +		/* Check nobody uses the timer, or already disabled/enabled */
>> +		ret = stm32_lptim_is_enabled(priv);
>> +		if ((ret < 0) || (!ret && !val))
>> +			return ret;
>> +		if (val && ret)
>> +			return -EBUSY;
>> +
>> +		ret = stm32_lptim_setup(priv, val);
>> +		if (ret)
>> +			return ret;
>> +		return stm32_lptim_set_enable_state(priv, val);
>> +
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> +static int stm32_lptim_read_raw(struct iio_dev *indio_dev,
>> +				struct iio_chan_spec const *chan,
>> +				int *val, int *val2, long mask)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +	u32 dat;
>> +	int ret;
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_RAW:
>> +		ret = regmap_read(priv->regmap, STM32_LPTIM_CNT, &dat);
>> +		if (ret)
>> +			return ret;
>> +		*val = dat;
>> +		return IIO_VAL_INT;
>> +
>> +	case IIO_CHAN_INFO_ENABLE:
>> +		ret = stm32_lptim_is_enabled(priv);
>> +		if (ret < 0)
>> +			return ret;
>> +		*val = ret;
>> +		return IIO_VAL_INT;
>> +
>> +	case IIO_CHAN_INFO_SCALE:
>> +		/* Non-quadrature mode: scale = 1 */
>> +		*val = 1;
>> +		*val2 = 0;
>> +		if (priv->quadrature_mode) {
>> +			/*
>> +			 * Quadrature encoder mode:
>> +			 * - both edges, quarter cycle, scale is 0.25
>> +			 * - either rising/falling edge scale is 0.5
>> +			 */
>> +			if (priv->polarity > 1)
>> +				*val2 = 2;
>> +			else
>> +				*val2 = 1;
>> +		}
>> +		return IIO_VAL_FRACTIONAL_LOG2;
>> +
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> +static const struct iio_info stm32_lptim_cnt_iio_info = {
>> +	.read_raw = stm32_lptim_read_raw,
>> +	.write_raw = stm32_lptim_write_raw,
>> +	.driver_module = THIS_MODULE,
>> +};
>> +
>> +static const char *const stm32_lptim_quadrature_modes[] = {
>> +	"non-quadrature",
>> +	"quadrature",
>> +};
>> +
>> +static int stm32_lptim_get_quadrature_mode(struct iio_dev *indio_dev,
>> +					   const struct iio_chan_spec *chan)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +
>> +	return priv->quadrature_mode;
>> +}
>> +
>> +static int stm32_lptim_set_quadrature_mode(struct iio_dev *indio_dev,
>> +					   const struct iio_chan_spec *chan,
>> +					   unsigned int type)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +
>> +	if (stm32_lptim_is_enabled(priv))
>> +		return -EBUSY;
>> +
>> +	priv->quadrature_mode = type;
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct iio_enum stm32_lptim_quadrature_mode_en = {
>> +	.items = stm32_lptim_quadrature_modes,
>> +	.num_items = ARRAY_SIZE(stm32_lptim_quadrature_modes),
>> +	.get = stm32_lptim_get_quadrature_mode,
>> +	.set = stm32_lptim_set_quadrature_mode,
>> +};
>> +
>> +static const char * const stm32_lptim_cnt_polarity[] = {
>> +	"rising-edge", "falling-edge", "both-edges",
>> +};
>> +
>> +static int stm32_lptim_cnt_get_polarity(struct iio_dev *indio_dev,
>> +					const struct iio_chan_spec *chan)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +
>> +	return priv->polarity;
>> +}
>> +
>> +static int stm32_lptim_cnt_set_polarity(struct iio_dev *indio_dev,
>> +					const struct iio_chan_spec *chan,
>> +					unsigned int type)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +
>> +	if (stm32_lptim_is_enabled(priv))
>> +		return -EBUSY;
>> +
>> +	priv->polarity = type;
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct iio_enum stm32_lptim_cnt_polarity_en = {
>> +	.items = stm32_lptim_cnt_polarity,
>> +	.num_items = ARRAY_SIZE(stm32_lptim_cnt_polarity),
>> +	.get = stm32_lptim_cnt_get_polarity,
>> +	.set = stm32_lptim_cnt_set_polarity,
>> +};
>> +
>> +static ssize_t stm32_lptim_cnt_get_preset(struct iio_dev *indio_dev,
>> +					  uintptr_t private,
>> +					  const struct iio_chan_spec *chan,
>> +					  char *buf)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +
>> +	return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset);
>> +}
>> +
>> +static ssize_t stm32_lptim_cnt_set_preset(struct iio_dev *indio_dev,
>> +					  uintptr_t private,
>> +					  const struct iio_chan_spec *chan,
>> +					  const char *buf, size_t len)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	if (stm32_lptim_is_enabled(priv))
>> +		return -EBUSY;
>> +
>> +	ret = kstrtouint(buf, 0, &priv->preset);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (priv->preset > STM32_LPTIM_MAX_ARR)
>> +		return -EINVAL;
>> +
>> +	return len;
>> +}
>> +
>> +/* LP timer with encoder */
>> +static const struct iio_chan_spec_ext_info stm32_lptim_enc_ext_info[] = {
>> +	{
>> +		.name = "preset",
>> +		.shared = IIO_SEPARATE,
>> +		.read = stm32_lptim_cnt_get_preset,
>> +		.write = stm32_lptim_cnt_set_preset,
>> +	},
>> +	IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
>> +	IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
>> +	IIO_ENUM("quadrature_mode", IIO_SEPARATE,
>> +		 &stm32_lptim_quadrature_mode_en),
>> +	IIO_ENUM_AVAILABLE("quadrature_mode", &stm32_lptim_quadrature_mode_en),
>> +	{}
>> +};
>> +
>> +static const struct iio_chan_spec stm32_lptim_enc_channels = {
>> +	.type = IIO_COUNT,
>> +	.channel = 0,
>> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>> +			      BIT(IIO_CHAN_INFO_ENABLE) |
>> +			      BIT(IIO_CHAN_INFO_SCALE),
>> +	.ext_info = stm32_lptim_enc_ext_info,
>> +	.indexed = 1,
>> +};
>> +
>> +/* LP timer without encoder (counter only) */
>> +static const struct iio_chan_spec_ext_info stm32_lptim_cnt_ext_info[] = {
>> +	{
>> +		.name = "preset",
>> +		.shared = IIO_SEPARATE,
>> +		.read = stm32_lptim_cnt_get_preset,
>> +		.write = stm32_lptim_cnt_set_preset,
>> +	},
>> +	IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
>> +	IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
>> +	{}
>> +};
>> +
>> +static const struct iio_chan_spec stm32_lptim_cnt_channels = {
>> +	.type = IIO_COUNT,
>> +	.channel = 0,
>> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>> +			      BIT(IIO_CHAN_INFO_ENABLE) |
>> +			      BIT(IIO_CHAN_INFO_SCALE),
>> +	.ext_info = stm32_lptim_cnt_ext_info,
>> +	.indexed = 1,
>> +};
>> +
>> +static int stm32_lptim_cnt_probe(struct platform_device *pdev)
>> +{
>> +	struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
>> +	struct stm32_lptim_cnt *priv;
>> +	struct iio_dev *indio_dev;
>> +
>> +	if (IS_ERR_OR_NULL(ddata))
>> +		return -EINVAL;
>> +
>> +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
>> +	if (!indio_dev)
>> +		return -ENOMEM;
>> +
>> +	priv = iio_priv(indio_dev);
>> +	priv->dev = &pdev->dev;
>> +	priv->regmap = ddata->regmap;
>> +	priv->clk = ddata->clk;
>> +	priv->preset = STM32_LPTIM_MAX_ARR;
>> +
>> +	indio_dev->name = dev_name(&pdev->dev);
>> +	indio_dev->dev.parent = &pdev->dev;
>> +	indio_dev->dev.of_node = pdev->dev.of_node;
>> +	indio_dev->info = &stm32_lptim_cnt_iio_info;
>> +	if (ddata->has_encoder)
>> +		indio_dev->channels = &stm32_lptim_enc_channels;
>> +	else
>> +		indio_dev->channels = &stm32_lptim_cnt_channels;
>> +	indio_dev->num_channels = 1;
>> +
>> +	platform_set_drvdata(pdev, priv);
>> +
>> +	return devm_iio_device_register(&pdev->dev, indio_dev);
>> +}
>> +
>> +static const struct of_device_id stm32_lptim_cnt_of_match[] = {
>> +	{ .compatible = "st,stm32-lptimer-counter", },
>> +	{},
>> +};
>> +MODULE_DEVICE_TABLE(of, stm32_lptim_cnt_of_match);
>> +
>> +static struct platform_driver stm32_lptim_cnt_driver = {
>> +	.probe = stm32_lptim_cnt_probe,
>> +	.driver = {
>> +		.name = "stm32-lptimer-counter",
>> +		.of_match_table = stm32_lptim_cnt_of_match,
>> +	},
>> +};
>> +module_platform_driver(stm32_lptim_cnt_driver);
>> +
>> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
>> +MODULE_ALIAS("platform:stm32-lptimer-counter");
>> +MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
>> +MODULE_LICENSE("GPL v2");
>

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 8/9] iio: counter: Add support for STM32 LPTimer
@ 2017-07-10 12:54       ` William Breathitt Gray
  0 siblings, 0 replies; 63+ messages in thread
From: William Breathitt Gray @ 2017-07-10 12:54 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Fabrice Gasnier, lee.jones, benjamin.gaignard, thierry.reding,
	robh+dt, mark.rutland, alexandre.torgue, mcoquelin.stm32,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm

On Sun, Jul 09, 2017 at 09:12:43PM +0100, Jonathan Cameron wrote:
>On Fri, 7 Jul 2017 18:31:07 +0200
>Fabrice Gasnier <fabrice.gasnier@st.com> wrote:
>
>> 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>
>So the obvious question on this one is whether to to delay merging
>until we have come to some decision on William's proposed new interface.
>
>My gut feeling is no - we are supporting this interface anyway going
>forward for a couple of devices.  Even if the new interface comes
>along reasonably quickly I don't have any problem supporting this
>approach for one more driver.
>
>So I'd like to let this sit for a little while longer ideally for other
>opinions.
>
From me however it is good to go.
>
>Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

For what it's worth, I don't think we should delay drivers just yet.
Although I plan on releasing the new generic counter and quadrature
counter interfaces in the coming weeks, we're certainly bound to go
through several revisions of them before they are acceptable for merge.

I wouldn't want the development of these interfaces to stall other
drivers, so maintaining the current approach with the anticipation of a
subsequent upgrade patch should be our course of action (at least until
the generic counter interface is solidified and merged).

William Breathitt Gray

>> ---
>> Changes in v2:
>> - s/Low Power/Low-Power
>> - update few comments
>> ---
>>  .../ABI/testing/sysfs-bus-iio-lptimer-stm32        |  57 +++
>>  drivers/iio/counter/Kconfig                        |   9 +
>>  drivers/iio/counter/Makefile                       |   1 +
>>  drivers/iio/counter/stm32-lptimer-cnt.c            | 383 +++++++++++++++++++++
>>  4 files changed, 450 insertions(+)
>>  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
>>  create mode 100644 drivers/iio/counter/stm32-lptimer-cnt.c
>> 
>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32 b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
>> new file mode 100644
>> index 0000000..ad2cc63
>> --- /dev/null
>> +++ b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
>> @@ -0,0 +1,57 @@
>> +What:		/sys/bus/iio/devices/iio:deviceX/in_count0_preset
>> +KernelVersion:	4.13
>> +Contact:	fabrice.gasnier@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
>So this is our one oddity.  I'm happy enough with the interface until if
>we come up with something better.  So lets go with this for now.
>> +
>> +		In non-quadrature mode, device counts up on active edge.
>> +		In quadrature mode, encoder counting scenarios are as follows:
>> +		----------------------------------------------------------------
>> +		| Active  | Level on |      IN1 signal    |     IN2 signal     |
>> +		| edge    | opposite |------------------------------------------
>> +		|         | signal   |  Rising  | Falling |  Rising  | Falling |
>> +		----------------------------------------------------------------
>> +		| Rising  | High ->  |   Down   |    -    |    Up    |    -    |
>> +		| edge    | Low  ->  |    Up    |    -    |   Down   |    -    |
>> +		----------------------------------------------------------------
>> +		| Falling | High ->  |    -     |    Up   |    -     |   Down  |
>> +		| edge    | Low  ->  |    -     |   Down  |    -     |    Up   |
>> +		----------------------------------------------------------------
>> +		| Both    | High ->  |   Down   |    Up   |    Up    |   Down  |
>> +		| edges   | Low  ->  |    Up    |   Down  |   Down   |    Up   |
>> +		----------------------------------------------------------------
>> diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
>> index b37e5fc..474e1ac 100644
>> --- a/drivers/iio/counter/Kconfig
>> +++ b/drivers/iio/counter/Kconfig
>> @@ -21,4 +21,13 @@ config 104_QUAD_8
>>  	  The base port addresses for the devices may be configured via the base
>>  	  array module parameter.
>>  
>> +config STM32_LPTIMER_CNT
>> +	tristate "STM32 LP Timer encoder counter driver"
>> +	depends on MFD_STM32_LPTIMER || COMPILE_TEST
>> +	help
>> +	  Select this option to enable STM32 Low-Power Timer quadrature encoder
>> +	  and counter driver.
>> +
>> +	  To compile this driver as a module, choose M here: the
>> +	  module will be called stm32-lptimer-cnt.
>>  endmenu
>> diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
>> index 007e884..1b9a896 100644
>> --- a/drivers/iio/counter/Makefile
>> +++ b/drivers/iio/counter/Makefile
>> @@ -5,3 +5,4 @@
>>  # When adding new entries keep the list in alphabetical order
>>  
>>  obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
>> +obj-$(CONFIG_STM32_LPTIMER_CNT)	+= stm32-lptimer-cnt.o
>> diff --git a/drivers/iio/counter/stm32-lptimer-cnt.c b/drivers/iio/counter/stm32-lptimer-cnt.c
>> new file mode 100644
>> index 0000000..1c5909b
>> --- /dev/null
>> +++ b/drivers/iio/counter/stm32-lptimer-cnt.c
>> @@ -0,0 +1,383 @@
>> +/*
>> + * STM32 Low-Power Timer Encoder and Counter driver
>> + *
>> + * Copyright (C) STMicroelectronics 2017
>> + *
>> + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>
>> + *
>> + * Inspired by 104-quad-8 and stm32-timer-trigger drivers.
>> + *
>> + * License terms:  GNU General Public License (GPL), version 2
>> + */
>> +
>> +#include <linux/bitfield.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/mfd/stm32-lptimer.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +
>> +struct stm32_lptim_cnt {
>> +	struct device *dev;
>> +	struct regmap *regmap;
>> +	struct clk *clk;
>> +	u32 preset;
>> +	u32 polarity;
>> +	u32 quadrature_mode;
>> +};
>> +
>> +static int stm32_lptim_is_enabled(struct stm32_lptim_cnt *priv)
>> +{
>> +	u32 val;
>> +	int ret;
>> +
>> +	ret = regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
>> +	if (ret)
>> +		return ret;
>> +
>> +	return FIELD_GET(STM32_LPTIM_ENABLE, val);
>> +}
>> +
>> +static int stm32_lptim_set_enable_state(struct stm32_lptim_cnt *priv,
>> +					int enable)
>> +{
>> +	int ret;
>> +	u32 val;
>> +
>> +	val = FIELD_PREP(STM32_LPTIM_ENABLE, enable);
>> +	ret = regmap_write(priv->regmap, STM32_LPTIM_CR, val);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (!enable) {
>> +		clk_disable(priv->clk);
>> +		return 0;
>> +	}
>> +
>> +	/* LP timer must be enabled before writing CMP & ARR */
>> +	ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, priv->preset);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, 0);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* ensure CMP & ARR registers are properly written */
>> +	ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val,
>> +				       (val & STM32_LPTIM_CMPOK_ARROK),
>> +				       100, 1000);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,
>> +			   STM32_LPTIM_CMPOKCF_ARROKCF);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = clk_enable(priv->clk);
>> +	if (ret) {
>> +		regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
>> +		return ret;
>> +	}
>> +
>> +	/* Start LP timer in continuous mode */
>> +	return regmap_update_bits(priv->regmap, STM32_LPTIM_CR,
>> +				  STM32_LPTIM_CNTSTRT, STM32_LPTIM_CNTSTRT);
>> +}
>> +
>> +static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
>> +{
>> +	u32 mask = STM32_LPTIM_ENC | STM32_LPTIM_COUNTMODE |
>> +		   STM32_LPTIM_CKPOL | STM32_LPTIM_PRESC;
>> +	u32 val;
>> +
>> +	/* Setup LP timer encoder/counter and polarity, without prescaler */
>> +	if (priv->quadrature_mode)
>> +		val = enable ? STM32_LPTIM_ENC : 0;
>> +	else
>> +		val = enable ? STM32_LPTIM_COUNTMODE : 0;
>> +	val |= FIELD_PREP(STM32_LPTIM_CKPOL, enable ? priv->polarity : 0);
>> +
>> +	return regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask, val);
>> +}
>> +
>> +static int stm32_lptim_write_raw(struct iio_dev *indio_dev,
>> +				 struct iio_chan_spec const *chan,
>> +				 int val, int val2, long mask)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_ENABLE:
>> +		if (val < 0 || val > 1)
>> +			return -EINVAL;
>> +
>> +		/* Check nobody uses the timer, or already disabled/enabled */
>> +		ret = stm32_lptim_is_enabled(priv);
>> +		if ((ret < 0) || (!ret && !val))
>> +			return ret;
>> +		if (val && ret)
>> +			return -EBUSY;
>> +
>> +		ret = stm32_lptim_setup(priv, val);
>> +		if (ret)
>> +			return ret;
>> +		return stm32_lptim_set_enable_state(priv, val);
>> +
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> +static int stm32_lptim_read_raw(struct iio_dev *indio_dev,
>> +				struct iio_chan_spec const *chan,
>> +				int *val, int *val2, long mask)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +	u32 dat;
>> +	int ret;
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_RAW:
>> +		ret = regmap_read(priv->regmap, STM32_LPTIM_CNT, &dat);
>> +		if (ret)
>> +			return ret;
>> +		*val = dat;
>> +		return IIO_VAL_INT;
>> +
>> +	case IIO_CHAN_INFO_ENABLE:
>> +		ret = stm32_lptim_is_enabled(priv);
>> +		if (ret < 0)
>> +			return ret;
>> +		*val = ret;
>> +		return IIO_VAL_INT;
>> +
>> +	case IIO_CHAN_INFO_SCALE:
>> +		/* Non-quadrature mode: scale = 1 */
>> +		*val = 1;
>> +		*val2 = 0;
>> +		if (priv->quadrature_mode) {
>> +			/*
>> +			 * Quadrature encoder mode:
>> +			 * - both edges, quarter cycle, scale is 0.25
>> +			 * - either rising/falling edge scale is 0.5
>> +			 */
>> +			if (priv->polarity > 1)
>> +				*val2 = 2;
>> +			else
>> +				*val2 = 1;
>> +		}
>> +		return IIO_VAL_FRACTIONAL_LOG2;
>> +
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> +static const struct iio_info stm32_lptim_cnt_iio_info = {
>> +	.read_raw = stm32_lptim_read_raw,
>> +	.write_raw = stm32_lptim_write_raw,
>> +	.driver_module = THIS_MODULE,
>> +};
>> +
>> +static const char *const stm32_lptim_quadrature_modes[] = {
>> +	"non-quadrature",
>> +	"quadrature",
>> +};
>> +
>> +static int stm32_lptim_get_quadrature_mode(struct iio_dev *indio_dev,
>> +					   const struct iio_chan_spec *chan)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +
>> +	return priv->quadrature_mode;
>> +}
>> +
>> +static int stm32_lptim_set_quadrature_mode(struct iio_dev *indio_dev,
>> +					   const struct iio_chan_spec *chan,
>> +					   unsigned int type)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +
>> +	if (stm32_lptim_is_enabled(priv))
>> +		return -EBUSY;
>> +
>> +	priv->quadrature_mode = type;
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct iio_enum stm32_lptim_quadrature_mode_en = {
>> +	.items = stm32_lptim_quadrature_modes,
>> +	.num_items = ARRAY_SIZE(stm32_lptim_quadrature_modes),
>> +	.get = stm32_lptim_get_quadrature_mode,
>> +	.set = stm32_lptim_set_quadrature_mode,
>> +};
>> +
>> +static const char * const stm32_lptim_cnt_polarity[] = {
>> +	"rising-edge", "falling-edge", "both-edges",
>> +};
>> +
>> +static int stm32_lptim_cnt_get_polarity(struct iio_dev *indio_dev,
>> +					const struct iio_chan_spec *chan)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +
>> +	return priv->polarity;
>> +}
>> +
>> +static int stm32_lptim_cnt_set_polarity(struct iio_dev *indio_dev,
>> +					const struct iio_chan_spec *chan,
>> +					unsigned int type)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +
>> +	if (stm32_lptim_is_enabled(priv))
>> +		return -EBUSY;
>> +
>> +	priv->polarity = type;
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct iio_enum stm32_lptim_cnt_polarity_en = {
>> +	.items = stm32_lptim_cnt_polarity,
>> +	.num_items = ARRAY_SIZE(stm32_lptim_cnt_polarity),
>> +	.get = stm32_lptim_cnt_get_polarity,
>> +	.set = stm32_lptim_cnt_set_polarity,
>> +};
>> +
>> +static ssize_t stm32_lptim_cnt_get_preset(struct iio_dev *indio_dev,
>> +					  uintptr_t private,
>> +					  const struct iio_chan_spec *chan,
>> +					  char *buf)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +
>> +	return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset);
>> +}
>> +
>> +static ssize_t stm32_lptim_cnt_set_preset(struct iio_dev *indio_dev,
>> +					  uintptr_t private,
>> +					  const struct iio_chan_spec *chan,
>> +					  const char *buf, size_t len)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	if (stm32_lptim_is_enabled(priv))
>> +		return -EBUSY;
>> +
>> +	ret = kstrtouint(buf, 0, &priv->preset);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (priv->preset > STM32_LPTIM_MAX_ARR)
>> +		return -EINVAL;
>> +
>> +	return len;
>> +}
>> +
>> +/* LP timer with encoder */
>> +static const struct iio_chan_spec_ext_info stm32_lptim_enc_ext_info[] = {
>> +	{
>> +		.name = "preset",
>> +		.shared = IIO_SEPARATE,
>> +		.read = stm32_lptim_cnt_get_preset,
>> +		.write = stm32_lptim_cnt_set_preset,
>> +	},
>> +	IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
>> +	IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
>> +	IIO_ENUM("quadrature_mode", IIO_SEPARATE,
>> +		 &stm32_lptim_quadrature_mode_en),
>> +	IIO_ENUM_AVAILABLE("quadrature_mode", &stm32_lptim_quadrature_mode_en),
>> +	{}
>> +};
>> +
>> +static const struct iio_chan_spec stm32_lptim_enc_channels = {
>> +	.type = IIO_COUNT,
>> +	.channel = 0,
>> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>> +			      BIT(IIO_CHAN_INFO_ENABLE) |
>> +			      BIT(IIO_CHAN_INFO_SCALE),
>> +	.ext_info = stm32_lptim_enc_ext_info,
>> +	.indexed = 1,
>> +};
>> +
>> +/* LP timer without encoder (counter only) */
>> +static const struct iio_chan_spec_ext_info stm32_lptim_cnt_ext_info[] = {
>> +	{
>> +		.name = "preset",
>> +		.shared = IIO_SEPARATE,
>> +		.read = stm32_lptim_cnt_get_preset,
>> +		.write = stm32_lptim_cnt_set_preset,
>> +	},
>> +	IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
>> +	IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
>> +	{}
>> +};
>> +
>> +static const struct iio_chan_spec stm32_lptim_cnt_channels = {
>> +	.type = IIO_COUNT,
>> +	.channel = 0,
>> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>> +			      BIT(IIO_CHAN_INFO_ENABLE) |
>> +			      BIT(IIO_CHAN_INFO_SCALE),
>> +	.ext_info = stm32_lptim_cnt_ext_info,
>> +	.indexed = 1,
>> +};
>> +
>> +static int stm32_lptim_cnt_probe(struct platform_device *pdev)
>> +{
>> +	struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
>> +	struct stm32_lptim_cnt *priv;
>> +	struct iio_dev *indio_dev;
>> +
>> +	if (IS_ERR_OR_NULL(ddata))
>> +		return -EINVAL;
>> +
>> +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
>> +	if (!indio_dev)
>> +		return -ENOMEM;
>> +
>> +	priv = iio_priv(indio_dev);
>> +	priv->dev = &pdev->dev;
>> +	priv->regmap = ddata->regmap;
>> +	priv->clk = ddata->clk;
>> +	priv->preset = STM32_LPTIM_MAX_ARR;
>> +
>> +	indio_dev->name = dev_name(&pdev->dev);
>> +	indio_dev->dev.parent = &pdev->dev;
>> +	indio_dev->dev.of_node = pdev->dev.of_node;
>> +	indio_dev->info = &stm32_lptim_cnt_iio_info;
>> +	if (ddata->has_encoder)
>> +		indio_dev->channels = &stm32_lptim_enc_channels;
>> +	else
>> +		indio_dev->channels = &stm32_lptim_cnt_channels;
>> +	indio_dev->num_channels = 1;
>> +
>> +	platform_set_drvdata(pdev, priv);
>> +
>> +	return devm_iio_device_register(&pdev->dev, indio_dev);
>> +}
>> +
>> +static const struct of_device_id stm32_lptim_cnt_of_match[] = {
>> +	{ .compatible = "st,stm32-lptimer-counter", },
>> +	{},
>> +};
>> +MODULE_DEVICE_TABLE(of, stm32_lptim_cnt_of_match);
>> +
>> +static struct platform_driver stm32_lptim_cnt_driver = {
>> +	.probe = stm32_lptim_cnt_probe,
>> +	.driver = {
>> +		.name = "stm32-lptimer-counter",
>> +		.of_match_table = stm32_lptim_cnt_of_match,
>> +	},
>> +};
>> +module_platform_driver(stm32_lptim_cnt_driver);
>> +
>> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
>> +MODULE_ALIAS("platform:stm32-lptimer-counter");
>> +MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
>> +MODULE_LICENSE("GPL v2");
>

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 8/9] iio: counter: Add support for STM32 LPTimer
@ 2017-07-10 12:54       ` William Breathitt Gray
  0 siblings, 0 replies; 63+ messages in thread
From: William Breathitt Gray @ 2017-07-10 12:54 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Fabrice Gasnier, lee.jones, benjamin.gaignard, thierry.reding,
	robh+dt, mark.rutland, alexandre.torgue, mcoquelin.stm32,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm

On Sun, Jul 09, 2017 at 09:12:43PM +0100, Jonathan Cameron wrote:
>On Fri, 7 Jul 2017 18:31:07 +0200
>Fabrice Gasnier <fabrice.gasnier@st.com> wrote:
>
>> 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>
>So the obvious question on this one is whether to to delay merging
>until we have come to some decision on William's proposed new interface.
>
>My gut feeling is no - we are supporting this interface anyway going
>forward for a couple of devices.  Even if the new interface comes
>along reasonably quickly I don't have any problem supporting this
>approach for one more driver.
>
>So I'd like to let this sit for a little while longer ideally for other
>opinions.
>
>>From me however it is good to go.
>
>Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

For what it's worth, I don't think we should delay drivers just yet.
Although I plan on releasing the new generic counter and quadrature
counter interfaces in the coming weeks, we're certainly bound to go
through several revisions of them before they are acceptable for merge.

I wouldn't want the development of these interfaces to stall other
drivers, so maintaining the current approach with the anticipation of a
subsequent upgrade patch should be our course of action (at least until
the generic counter interface is solidified and merged).

William Breathitt Gray

>> ---
>> Changes in v2:
>> - s/Low Power/Low-Power
>> - update few comments
>> ---
>>  .../ABI/testing/sysfs-bus-iio-lptimer-stm32        |  57 +++
>>  drivers/iio/counter/Kconfig                        |   9 +
>>  drivers/iio/counter/Makefile                       |   1 +
>>  drivers/iio/counter/stm32-lptimer-cnt.c            | 383 +++++++++++++++++++++
>>  4 files changed, 450 insertions(+)
>>  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
>>  create mode 100644 drivers/iio/counter/stm32-lptimer-cnt.c
>> 
>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32 b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
>> new file mode 100644
>> index 0000000..ad2cc63
>> --- /dev/null
>> +++ b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
>> @@ -0,0 +1,57 @@
>> +What:		/sys/bus/iio/devices/iio:deviceX/in_count0_preset
>> +KernelVersion:	4.13
>> +Contact:	fabrice.gasnier@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
>So this is our one oddity.  I'm happy enough with the interface until if
>we come up with something better.  So lets go with this for now.
>> +
>> +		In non-quadrature mode, device counts up on active edge.
>> +		In quadrature mode, encoder counting scenarios are as follows:
>> +		----------------------------------------------------------------
>> +		| Active  | Level on |      IN1 signal    |     IN2 signal     |
>> +		| edge    | opposite |------------------------------------------
>> +		|         | signal   |  Rising  | Falling |  Rising  | Falling |
>> +		----------------------------------------------------------------
>> +		| Rising  | High ->  |   Down   |    -    |    Up    |    -    |
>> +		| edge    | Low  ->  |    Up    |    -    |   Down   |    -    |
>> +		----------------------------------------------------------------
>> +		| Falling | High ->  |    -     |    Up   |    -     |   Down  |
>> +		| edge    | Low  ->  |    -     |   Down  |    -     |    Up   |
>> +		----------------------------------------------------------------
>> +		| Both    | High ->  |   Down   |    Up   |    Up    |   Down  |
>> +		| edges   | Low  ->  |    Up    |   Down  |   Down   |    Up   |
>> +		----------------------------------------------------------------
>> diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
>> index b37e5fc..474e1ac 100644
>> --- a/drivers/iio/counter/Kconfig
>> +++ b/drivers/iio/counter/Kconfig
>> @@ -21,4 +21,13 @@ config 104_QUAD_8
>>  	  The base port addresses for the devices may be configured via the base
>>  	  array module parameter.
>>  
>> +config STM32_LPTIMER_CNT
>> +	tristate "STM32 LP Timer encoder counter driver"
>> +	depends on MFD_STM32_LPTIMER || COMPILE_TEST
>> +	help
>> +	  Select this option to enable STM32 Low-Power Timer quadrature encoder
>> +	  and counter driver.
>> +
>> +	  To compile this driver as a module, choose M here: the
>> +	  module will be called stm32-lptimer-cnt.
>>  endmenu
>> diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
>> index 007e884..1b9a896 100644
>> --- a/drivers/iio/counter/Makefile
>> +++ b/drivers/iio/counter/Makefile
>> @@ -5,3 +5,4 @@
>>  # When adding new entries keep the list in alphabetical order
>>  
>>  obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
>> +obj-$(CONFIG_STM32_LPTIMER_CNT)	+= stm32-lptimer-cnt.o
>> diff --git a/drivers/iio/counter/stm32-lptimer-cnt.c b/drivers/iio/counter/stm32-lptimer-cnt.c
>> new file mode 100644
>> index 0000000..1c5909b
>> --- /dev/null
>> +++ b/drivers/iio/counter/stm32-lptimer-cnt.c
>> @@ -0,0 +1,383 @@
>> +/*
>> + * STM32 Low-Power Timer Encoder and Counter driver
>> + *
>> + * Copyright (C) STMicroelectronics 2017
>> + *
>> + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>
>> + *
>> + * Inspired by 104-quad-8 and stm32-timer-trigger drivers.
>> + *
>> + * License terms:  GNU General Public License (GPL), version 2
>> + */
>> +
>> +#include <linux/bitfield.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/mfd/stm32-lptimer.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +
>> +struct stm32_lptim_cnt {
>> +	struct device *dev;
>> +	struct regmap *regmap;
>> +	struct clk *clk;
>> +	u32 preset;
>> +	u32 polarity;
>> +	u32 quadrature_mode;
>> +};
>> +
>> +static int stm32_lptim_is_enabled(struct stm32_lptim_cnt *priv)
>> +{
>> +	u32 val;
>> +	int ret;
>> +
>> +	ret = regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
>> +	if (ret)
>> +		return ret;
>> +
>> +	return FIELD_GET(STM32_LPTIM_ENABLE, val);
>> +}
>> +
>> +static int stm32_lptim_set_enable_state(struct stm32_lptim_cnt *priv,
>> +					int enable)
>> +{
>> +	int ret;
>> +	u32 val;
>> +
>> +	val = FIELD_PREP(STM32_LPTIM_ENABLE, enable);
>> +	ret = regmap_write(priv->regmap, STM32_LPTIM_CR, val);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (!enable) {
>> +		clk_disable(priv->clk);
>> +		return 0;
>> +	}
>> +
>> +	/* LP timer must be enabled before writing CMP & ARR */
>> +	ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, priv->preset);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, 0);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* ensure CMP & ARR registers are properly written */
>> +	ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val,
>> +				       (val & STM32_LPTIM_CMPOK_ARROK),
>> +				       100, 1000);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,
>> +			   STM32_LPTIM_CMPOKCF_ARROKCF);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = clk_enable(priv->clk);
>> +	if (ret) {
>> +		regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
>> +		return ret;
>> +	}
>> +
>> +	/* Start LP timer in continuous mode */
>> +	return regmap_update_bits(priv->regmap, STM32_LPTIM_CR,
>> +				  STM32_LPTIM_CNTSTRT, STM32_LPTIM_CNTSTRT);
>> +}
>> +
>> +static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
>> +{
>> +	u32 mask = STM32_LPTIM_ENC | STM32_LPTIM_COUNTMODE |
>> +		   STM32_LPTIM_CKPOL | STM32_LPTIM_PRESC;
>> +	u32 val;
>> +
>> +	/* Setup LP timer encoder/counter and polarity, without prescaler */
>> +	if (priv->quadrature_mode)
>> +		val = enable ? STM32_LPTIM_ENC : 0;
>> +	else
>> +		val = enable ? STM32_LPTIM_COUNTMODE : 0;
>> +	val |= FIELD_PREP(STM32_LPTIM_CKPOL, enable ? priv->polarity : 0);
>> +
>> +	return regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask, val);
>> +}
>> +
>> +static int stm32_lptim_write_raw(struct iio_dev *indio_dev,
>> +				 struct iio_chan_spec const *chan,
>> +				 int val, int val2, long mask)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_ENABLE:
>> +		if (val < 0 || val > 1)
>> +			return -EINVAL;
>> +
>> +		/* Check nobody uses the timer, or already disabled/enabled */
>> +		ret = stm32_lptim_is_enabled(priv);
>> +		if ((ret < 0) || (!ret && !val))
>> +			return ret;
>> +		if (val && ret)
>> +			return -EBUSY;
>> +
>> +		ret = stm32_lptim_setup(priv, val);
>> +		if (ret)
>> +			return ret;
>> +		return stm32_lptim_set_enable_state(priv, val);
>> +
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> +static int stm32_lptim_read_raw(struct iio_dev *indio_dev,
>> +				struct iio_chan_spec const *chan,
>> +				int *val, int *val2, long mask)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +	u32 dat;
>> +	int ret;
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_RAW:
>> +		ret = regmap_read(priv->regmap, STM32_LPTIM_CNT, &dat);
>> +		if (ret)
>> +			return ret;
>> +		*val = dat;
>> +		return IIO_VAL_INT;
>> +
>> +	case IIO_CHAN_INFO_ENABLE:
>> +		ret = stm32_lptim_is_enabled(priv);
>> +		if (ret < 0)
>> +			return ret;
>> +		*val = ret;
>> +		return IIO_VAL_INT;
>> +
>> +	case IIO_CHAN_INFO_SCALE:
>> +		/* Non-quadrature mode: scale = 1 */
>> +		*val = 1;
>> +		*val2 = 0;
>> +		if (priv->quadrature_mode) {
>> +			/*
>> +			 * Quadrature encoder mode:
>> +			 * - both edges, quarter cycle, scale is 0.25
>> +			 * - either rising/falling edge scale is 0.5
>> +			 */
>> +			if (priv->polarity > 1)
>> +				*val2 = 2;
>> +			else
>> +				*val2 = 1;
>> +		}
>> +		return IIO_VAL_FRACTIONAL_LOG2;
>> +
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> +static const struct iio_info stm32_lptim_cnt_iio_info = {
>> +	.read_raw = stm32_lptim_read_raw,
>> +	.write_raw = stm32_lptim_write_raw,
>> +	.driver_module = THIS_MODULE,
>> +};
>> +
>> +static const char *const stm32_lptim_quadrature_modes[] = {
>> +	"non-quadrature",
>> +	"quadrature",
>> +};
>> +
>> +static int stm32_lptim_get_quadrature_mode(struct iio_dev *indio_dev,
>> +					   const struct iio_chan_spec *chan)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +
>> +	return priv->quadrature_mode;
>> +}
>> +
>> +static int stm32_lptim_set_quadrature_mode(struct iio_dev *indio_dev,
>> +					   const struct iio_chan_spec *chan,
>> +					   unsigned int type)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +
>> +	if (stm32_lptim_is_enabled(priv))
>> +		return -EBUSY;
>> +
>> +	priv->quadrature_mode = type;
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct iio_enum stm32_lptim_quadrature_mode_en = {
>> +	.items = stm32_lptim_quadrature_modes,
>> +	.num_items = ARRAY_SIZE(stm32_lptim_quadrature_modes),
>> +	.get = stm32_lptim_get_quadrature_mode,
>> +	.set = stm32_lptim_set_quadrature_mode,
>> +};
>> +
>> +static const char * const stm32_lptim_cnt_polarity[] = {
>> +	"rising-edge", "falling-edge", "both-edges",
>> +};
>> +
>> +static int stm32_lptim_cnt_get_polarity(struct iio_dev *indio_dev,
>> +					const struct iio_chan_spec *chan)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +
>> +	return priv->polarity;
>> +}
>> +
>> +static int stm32_lptim_cnt_set_polarity(struct iio_dev *indio_dev,
>> +					const struct iio_chan_spec *chan,
>> +					unsigned int type)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +
>> +	if (stm32_lptim_is_enabled(priv))
>> +		return -EBUSY;
>> +
>> +	priv->polarity = type;
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct iio_enum stm32_lptim_cnt_polarity_en = {
>> +	.items = stm32_lptim_cnt_polarity,
>> +	.num_items = ARRAY_SIZE(stm32_lptim_cnt_polarity),
>> +	.get = stm32_lptim_cnt_get_polarity,
>> +	.set = stm32_lptim_cnt_set_polarity,
>> +};
>> +
>> +static ssize_t stm32_lptim_cnt_get_preset(struct iio_dev *indio_dev,
>> +					  uintptr_t private,
>> +					  const struct iio_chan_spec *chan,
>> +					  char *buf)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +
>> +	return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset);
>> +}
>> +
>> +static ssize_t stm32_lptim_cnt_set_preset(struct iio_dev *indio_dev,
>> +					  uintptr_t private,
>> +					  const struct iio_chan_spec *chan,
>> +					  const char *buf, size_t len)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	if (stm32_lptim_is_enabled(priv))
>> +		return -EBUSY;
>> +
>> +	ret = kstrtouint(buf, 0, &priv->preset);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (priv->preset > STM32_LPTIM_MAX_ARR)
>> +		return -EINVAL;
>> +
>> +	return len;
>> +}
>> +
>> +/* LP timer with encoder */
>> +static const struct iio_chan_spec_ext_info stm32_lptim_enc_ext_info[] = {
>> +	{
>> +		.name = "preset",
>> +		.shared = IIO_SEPARATE,
>> +		.read = stm32_lptim_cnt_get_preset,
>> +		.write = stm32_lptim_cnt_set_preset,
>> +	},
>> +	IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
>> +	IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
>> +	IIO_ENUM("quadrature_mode", IIO_SEPARATE,
>> +		 &stm32_lptim_quadrature_mode_en),
>> +	IIO_ENUM_AVAILABLE("quadrature_mode", &stm32_lptim_quadrature_mode_en),
>> +	{}
>> +};
>> +
>> +static const struct iio_chan_spec stm32_lptim_enc_channels = {
>> +	.type = IIO_COUNT,
>> +	.channel = 0,
>> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>> +			      BIT(IIO_CHAN_INFO_ENABLE) |
>> +			      BIT(IIO_CHAN_INFO_SCALE),
>> +	.ext_info = stm32_lptim_enc_ext_info,
>> +	.indexed = 1,
>> +};
>> +
>> +/* LP timer without encoder (counter only) */
>> +static const struct iio_chan_spec_ext_info stm32_lptim_cnt_ext_info[] = {
>> +	{
>> +		.name = "preset",
>> +		.shared = IIO_SEPARATE,
>> +		.read = stm32_lptim_cnt_get_preset,
>> +		.write = stm32_lptim_cnt_set_preset,
>> +	},
>> +	IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
>> +	IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
>> +	{}
>> +};
>> +
>> +static const struct iio_chan_spec stm32_lptim_cnt_channels = {
>> +	.type = IIO_COUNT,
>> +	.channel = 0,
>> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>> +			      BIT(IIO_CHAN_INFO_ENABLE) |
>> +			      BIT(IIO_CHAN_INFO_SCALE),
>> +	.ext_info = stm32_lptim_cnt_ext_info,
>> +	.indexed = 1,
>> +};
>> +
>> +static int stm32_lptim_cnt_probe(struct platform_device *pdev)
>> +{
>> +	struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
>> +	struct stm32_lptim_cnt *priv;
>> +	struct iio_dev *indio_dev;
>> +
>> +	if (IS_ERR_OR_NULL(ddata))
>> +		return -EINVAL;
>> +
>> +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
>> +	if (!indio_dev)
>> +		return -ENOMEM;
>> +
>> +	priv = iio_priv(indio_dev);
>> +	priv->dev = &pdev->dev;
>> +	priv->regmap = ddata->regmap;
>> +	priv->clk = ddata->clk;
>> +	priv->preset = STM32_LPTIM_MAX_ARR;
>> +
>> +	indio_dev->name = dev_name(&pdev->dev);
>> +	indio_dev->dev.parent = &pdev->dev;
>> +	indio_dev->dev.of_node = pdev->dev.of_node;
>> +	indio_dev->info = &stm32_lptim_cnt_iio_info;
>> +	if (ddata->has_encoder)
>> +		indio_dev->channels = &stm32_lptim_enc_channels;
>> +	else
>> +		indio_dev->channels = &stm32_lptim_cnt_channels;
>> +	indio_dev->num_channels = 1;
>> +
>> +	platform_set_drvdata(pdev, priv);
>> +
>> +	return devm_iio_device_register(&pdev->dev, indio_dev);
>> +}
>> +
>> +static const struct of_device_id stm32_lptim_cnt_of_match[] = {
>> +	{ .compatible = "st,stm32-lptimer-counter", },
>> +	{},
>> +};
>> +MODULE_DEVICE_TABLE(of, stm32_lptim_cnt_of_match);
>> +
>> +static struct platform_driver stm32_lptim_cnt_driver = {
>> +	.probe = stm32_lptim_cnt_probe,
>> +	.driver = {
>> +		.name = "stm32-lptimer-counter",
>> +		.of_match_table = stm32_lptim_cnt_of_match,
>> +	},
>> +};
>> +module_platform_driver(stm32_lptim_cnt_driver);
>> +
>> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
>> +MODULE_ALIAS("platform:stm32-lptimer-counter");
>> +MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
>> +MODULE_LICENSE("GPL v2");
>

^ permalink raw reply	[flat|nested] 63+ messages in thread

* [PATCH v3 8/9] iio: counter: Add support for STM32 LPTimer
@ 2017-07-10 12:54       ` William Breathitt Gray
  0 siblings, 0 replies; 63+ messages in thread
From: William Breathitt Gray @ 2017-07-10 12:54 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, Jul 09, 2017 at 09:12:43PM +0100, Jonathan Cameron wrote:
>On Fri, 7 Jul 2017 18:31:07 +0200
>Fabrice Gasnier <fabrice.gasnier@st.com> wrote:
>
>> 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>
>So the obvious question on this one is whether to to delay merging
>until we have come to some decision on William's proposed new interface.
>
>My gut feeling is no - we are supporting this interface anyway going
>forward for a couple of devices.  Even if the new interface comes
>along reasonably quickly I don't have any problem supporting this
>approach for one more driver.
>
>So I'd like to let this sit for a little while longer ideally for other
>opinions.
>
>From me however it is good to go.
>
>Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

For what it's worth, I don't think we should delay drivers just yet.
Although I plan on releasing the new generic counter and quadrature
counter interfaces in the coming weeks, we're certainly bound to go
through several revisions of them before they are acceptable for merge.

I wouldn't want the development of these interfaces to stall other
drivers, so maintaining the current approach with the anticipation of a
subsequent upgrade patch should be our course of action (at least until
the generic counter interface is solidified and merged).

William Breathitt Gray

>> ---
>> Changes in v2:
>> - s/Low Power/Low-Power
>> - update few comments
>> ---
>>  .../ABI/testing/sysfs-bus-iio-lptimer-stm32        |  57 +++
>>  drivers/iio/counter/Kconfig                        |   9 +
>>  drivers/iio/counter/Makefile                       |   1 +
>>  drivers/iio/counter/stm32-lptimer-cnt.c            | 383 +++++++++++++++++++++
>>  4 files changed, 450 insertions(+)
>>  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
>>  create mode 100644 drivers/iio/counter/stm32-lptimer-cnt.c
>> 
>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32 b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
>> new file mode 100644
>> index 0000000..ad2cc63
>> --- /dev/null
>> +++ b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32
>> @@ -0,0 +1,57 @@
>> +What:		/sys/bus/iio/devices/iio:deviceX/in_count0_preset
>> +KernelVersion:	4.13
>> +Contact:	fabrice.gasnier at 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 at 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 at 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 at 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 at st.com
>> +Description:
>> +		Configure the device encoder/counter active edge:
>> +		- rising-edge
>> +		- falling-edge
>> +		- both-edges
>So this is our one oddity.  I'm happy enough with the interface until if
>we come up with something better.  So lets go with this for now.
>> +
>> +		In non-quadrature mode, device counts up on active edge.
>> +		In quadrature mode, encoder counting scenarios are as follows:
>> +		----------------------------------------------------------------
>> +		| Active  | Level on |      IN1 signal    |     IN2 signal     |
>> +		| edge    | opposite |------------------------------------------
>> +		|         | signal   |  Rising  | Falling |  Rising  | Falling |
>> +		----------------------------------------------------------------
>> +		| Rising  | High ->  |   Down   |    -    |    Up    |    -    |
>> +		| edge    | Low  ->  |    Up    |    -    |   Down   |    -    |
>> +		----------------------------------------------------------------
>> +		| Falling | High ->  |    -     |    Up   |    -     |   Down  |
>> +		| edge    | Low  ->  |    -     |   Down  |    -     |    Up   |
>> +		----------------------------------------------------------------
>> +		| Both    | High ->  |   Down   |    Up   |    Up    |   Down  |
>> +		| edges   | Low  ->  |    Up    |   Down  |   Down   |    Up   |
>> +		----------------------------------------------------------------
>> diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
>> index b37e5fc..474e1ac 100644
>> --- a/drivers/iio/counter/Kconfig
>> +++ b/drivers/iio/counter/Kconfig
>> @@ -21,4 +21,13 @@ config 104_QUAD_8
>>  	  The base port addresses for the devices may be configured via the base
>>  	  array module parameter.
>>  
>> +config STM32_LPTIMER_CNT
>> +	tristate "STM32 LP Timer encoder counter driver"
>> +	depends on MFD_STM32_LPTIMER || COMPILE_TEST
>> +	help
>> +	  Select this option to enable STM32 Low-Power Timer quadrature encoder
>> +	  and counter driver.
>> +
>> +	  To compile this driver as a module, choose M here: the
>> +	  module will be called stm32-lptimer-cnt.
>>  endmenu
>> diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
>> index 007e884..1b9a896 100644
>> --- a/drivers/iio/counter/Makefile
>> +++ b/drivers/iio/counter/Makefile
>> @@ -5,3 +5,4 @@
>>  # When adding new entries keep the list in alphabetical order
>>  
>>  obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
>> +obj-$(CONFIG_STM32_LPTIMER_CNT)	+= stm32-lptimer-cnt.o
>> diff --git a/drivers/iio/counter/stm32-lptimer-cnt.c b/drivers/iio/counter/stm32-lptimer-cnt.c
>> new file mode 100644
>> index 0000000..1c5909b
>> --- /dev/null
>> +++ b/drivers/iio/counter/stm32-lptimer-cnt.c
>> @@ -0,0 +1,383 @@
>> +/*
>> + * STM32 Low-Power Timer Encoder and Counter driver
>> + *
>> + * Copyright (C) STMicroelectronics 2017
>> + *
>> + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>
>> + *
>> + * Inspired by 104-quad-8 and stm32-timer-trigger drivers.
>> + *
>> + * License terms:  GNU General Public License (GPL), version 2
>> + */
>> +
>> +#include <linux/bitfield.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/mfd/stm32-lptimer.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +
>> +struct stm32_lptim_cnt {
>> +	struct device *dev;
>> +	struct regmap *regmap;
>> +	struct clk *clk;
>> +	u32 preset;
>> +	u32 polarity;
>> +	u32 quadrature_mode;
>> +};
>> +
>> +static int stm32_lptim_is_enabled(struct stm32_lptim_cnt *priv)
>> +{
>> +	u32 val;
>> +	int ret;
>> +
>> +	ret = regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
>> +	if (ret)
>> +		return ret;
>> +
>> +	return FIELD_GET(STM32_LPTIM_ENABLE, val);
>> +}
>> +
>> +static int stm32_lptim_set_enable_state(struct stm32_lptim_cnt *priv,
>> +					int enable)
>> +{
>> +	int ret;
>> +	u32 val;
>> +
>> +	val = FIELD_PREP(STM32_LPTIM_ENABLE, enable);
>> +	ret = regmap_write(priv->regmap, STM32_LPTIM_CR, val);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (!enable) {
>> +		clk_disable(priv->clk);
>> +		return 0;
>> +	}
>> +
>> +	/* LP timer must be enabled before writing CMP & ARR */
>> +	ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, priv->preset);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, 0);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* ensure CMP & ARR registers are properly written */
>> +	ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val,
>> +				       (val & STM32_LPTIM_CMPOK_ARROK),
>> +				       100, 1000);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,
>> +			   STM32_LPTIM_CMPOKCF_ARROKCF);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = clk_enable(priv->clk);
>> +	if (ret) {
>> +		regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
>> +		return ret;
>> +	}
>> +
>> +	/* Start LP timer in continuous mode */
>> +	return regmap_update_bits(priv->regmap, STM32_LPTIM_CR,
>> +				  STM32_LPTIM_CNTSTRT, STM32_LPTIM_CNTSTRT);
>> +}
>> +
>> +static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
>> +{
>> +	u32 mask = STM32_LPTIM_ENC | STM32_LPTIM_COUNTMODE |
>> +		   STM32_LPTIM_CKPOL | STM32_LPTIM_PRESC;
>> +	u32 val;
>> +
>> +	/* Setup LP timer encoder/counter and polarity, without prescaler */
>> +	if (priv->quadrature_mode)
>> +		val = enable ? STM32_LPTIM_ENC : 0;
>> +	else
>> +		val = enable ? STM32_LPTIM_COUNTMODE : 0;
>> +	val |= FIELD_PREP(STM32_LPTIM_CKPOL, enable ? priv->polarity : 0);
>> +
>> +	return regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask, val);
>> +}
>> +
>> +static int stm32_lptim_write_raw(struct iio_dev *indio_dev,
>> +				 struct iio_chan_spec const *chan,
>> +				 int val, int val2, long mask)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_ENABLE:
>> +		if (val < 0 || val > 1)
>> +			return -EINVAL;
>> +
>> +		/* Check nobody uses the timer, or already disabled/enabled */
>> +		ret = stm32_lptim_is_enabled(priv);
>> +		if ((ret < 0) || (!ret && !val))
>> +			return ret;
>> +		if (val && ret)
>> +			return -EBUSY;
>> +
>> +		ret = stm32_lptim_setup(priv, val);
>> +		if (ret)
>> +			return ret;
>> +		return stm32_lptim_set_enable_state(priv, val);
>> +
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> +static int stm32_lptim_read_raw(struct iio_dev *indio_dev,
>> +				struct iio_chan_spec const *chan,
>> +				int *val, int *val2, long mask)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +	u32 dat;
>> +	int ret;
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_RAW:
>> +		ret = regmap_read(priv->regmap, STM32_LPTIM_CNT, &dat);
>> +		if (ret)
>> +			return ret;
>> +		*val = dat;
>> +		return IIO_VAL_INT;
>> +
>> +	case IIO_CHAN_INFO_ENABLE:
>> +		ret = stm32_lptim_is_enabled(priv);
>> +		if (ret < 0)
>> +			return ret;
>> +		*val = ret;
>> +		return IIO_VAL_INT;
>> +
>> +	case IIO_CHAN_INFO_SCALE:
>> +		/* Non-quadrature mode: scale = 1 */
>> +		*val = 1;
>> +		*val2 = 0;
>> +		if (priv->quadrature_mode) {
>> +			/*
>> +			 * Quadrature encoder mode:
>> +			 * - both edges, quarter cycle, scale is 0.25
>> +			 * - either rising/falling edge scale is 0.5
>> +			 */
>> +			if (priv->polarity > 1)
>> +				*val2 = 2;
>> +			else
>> +				*val2 = 1;
>> +		}
>> +		return IIO_VAL_FRACTIONAL_LOG2;
>> +
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> +static const struct iio_info stm32_lptim_cnt_iio_info = {
>> +	.read_raw = stm32_lptim_read_raw,
>> +	.write_raw = stm32_lptim_write_raw,
>> +	.driver_module = THIS_MODULE,
>> +};
>> +
>> +static const char *const stm32_lptim_quadrature_modes[] = {
>> +	"non-quadrature",
>> +	"quadrature",
>> +};
>> +
>> +static int stm32_lptim_get_quadrature_mode(struct iio_dev *indio_dev,
>> +					   const struct iio_chan_spec *chan)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +
>> +	return priv->quadrature_mode;
>> +}
>> +
>> +static int stm32_lptim_set_quadrature_mode(struct iio_dev *indio_dev,
>> +					   const struct iio_chan_spec *chan,
>> +					   unsigned int type)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +
>> +	if (stm32_lptim_is_enabled(priv))
>> +		return -EBUSY;
>> +
>> +	priv->quadrature_mode = type;
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct iio_enum stm32_lptim_quadrature_mode_en = {
>> +	.items = stm32_lptim_quadrature_modes,
>> +	.num_items = ARRAY_SIZE(stm32_lptim_quadrature_modes),
>> +	.get = stm32_lptim_get_quadrature_mode,
>> +	.set = stm32_lptim_set_quadrature_mode,
>> +};
>> +
>> +static const char * const stm32_lptim_cnt_polarity[] = {
>> +	"rising-edge", "falling-edge", "both-edges",
>> +};
>> +
>> +static int stm32_lptim_cnt_get_polarity(struct iio_dev *indio_dev,
>> +					const struct iio_chan_spec *chan)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +
>> +	return priv->polarity;
>> +}
>> +
>> +static int stm32_lptim_cnt_set_polarity(struct iio_dev *indio_dev,
>> +					const struct iio_chan_spec *chan,
>> +					unsigned int type)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +
>> +	if (stm32_lptim_is_enabled(priv))
>> +		return -EBUSY;
>> +
>> +	priv->polarity = type;
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct iio_enum stm32_lptim_cnt_polarity_en = {
>> +	.items = stm32_lptim_cnt_polarity,
>> +	.num_items = ARRAY_SIZE(stm32_lptim_cnt_polarity),
>> +	.get = stm32_lptim_cnt_get_polarity,
>> +	.set = stm32_lptim_cnt_set_polarity,
>> +};
>> +
>> +static ssize_t stm32_lptim_cnt_get_preset(struct iio_dev *indio_dev,
>> +					  uintptr_t private,
>> +					  const struct iio_chan_spec *chan,
>> +					  char *buf)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +
>> +	return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset);
>> +}
>> +
>> +static ssize_t stm32_lptim_cnt_set_preset(struct iio_dev *indio_dev,
>> +					  uintptr_t private,
>> +					  const struct iio_chan_spec *chan,
>> +					  const char *buf, size_t len)
>> +{
>> +	struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	if (stm32_lptim_is_enabled(priv))
>> +		return -EBUSY;
>> +
>> +	ret = kstrtouint(buf, 0, &priv->preset);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (priv->preset > STM32_LPTIM_MAX_ARR)
>> +		return -EINVAL;
>> +
>> +	return len;
>> +}
>> +
>> +/* LP timer with encoder */
>> +static const struct iio_chan_spec_ext_info stm32_lptim_enc_ext_info[] = {
>> +	{
>> +		.name = "preset",
>> +		.shared = IIO_SEPARATE,
>> +		.read = stm32_lptim_cnt_get_preset,
>> +		.write = stm32_lptim_cnt_set_preset,
>> +	},
>> +	IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
>> +	IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
>> +	IIO_ENUM("quadrature_mode", IIO_SEPARATE,
>> +		 &stm32_lptim_quadrature_mode_en),
>> +	IIO_ENUM_AVAILABLE("quadrature_mode", &stm32_lptim_quadrature_mode_en),
>> +	{}
>> +};
>> +
>> +static const struct iio_chan_spec stm32_lptim_enc_channels = {
>> +	.type = IIO_COUNT,
>> +	.channel = 0,
>> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>> +			      BIT(IIO_CHAN_INFO_ENABLE) |
>> +			      BIT(IIO_CHAN_INFO_SCALE),
>> +	.ext_info = stm32_lptim_enc_ext_info,
>> +	.indexed = 1,
>> +};
>> +
>> +/* LP timer without encoder (counter only) */
>> +static const struct iio_chan_spec_ext_info stm32_lptim_cnt_ext_info[] = {
>> +	{
>> +		.name = "preset",
>> +		.shared = IIO_SEPARATE,
>> +		.read = stm32_lptim_cnt_get_preset,
>> +		.write = stm32_lptim_cnt_set_preset,
>> +	},
>> +	IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
>> +	IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
>> +	{}
>> +};
>> +
>> +static const struct iio_chan_spec stm32_lptim_cnt_channels = {
>> +	.type = IIO_COUNT,
>> +	.channel = 0,
>> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>> +			      BIT(IIO_CHAN_INFO_ENABLE) |
>> +			      BIT(IIO_CHAN_INFO_SCALE),
>> +	.ext_info = stm32_lptim_cnt_ext_info,
>> +	.indexed = 1,
>> +};
>> +
>> +static int stm32_lptim_cnt_probe(struct platform_device *pdev)
>> +{
>> +	struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
>> +	struct stm32_lptim_cnt *priv;
>> +	struct iio_dev *indio_dev;
>> +
>> +	if (IS_ERR_OR_NULL(ddata))
>> +		return -EINVAL;
>> +
>> +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
>> +	if (!indio_dev)
>> +		return -ENOMEM;
>> +
>> +	priv = iio_priv(indio_dev);
>> +	priv->dev = &pdev->dev;
>> +	priv->regmap = ddata->regmap;
>> +	priv->clk = ddata->clk;
>> +	priv->preset = STM32_LPTIM_MAX_ARR;
>> +
>> +	indio_dev->name = dev_name(&pdev->dev);
>> +	indio_dev->dev.parent = &pdev->dev;
>> +	indio_dev->dev.of_node = pdev->dev.of_node;
>> +	indio_dev->info = &stm32_lptim_cnt_iio_info;
>> +	if (ddata->has_encoder)
>> +		indio_dev->channels = &stm32_lptim_enc_channels;
>> +	else
>> +		indio_dev->channels = &stm32_lptim_cnt_channels;
>> +	indio_dev->num_channels = 1;
>> +
>> +	platform_set_drvdata(pdev, priv);
>> +
>> +	return devm_iio_device_register(&pdev->dev, indio_dev);
>> +}
>> +
>> +static const struct of_device_id stm32_lptim_cnt_of_match[] = {
>> +	{ .compatible = "st,stm32-lptimer-counter", },
>> +	{},
>> +};
>> +MODULE_DEVICE_TABLE(of, stm32_lptim_cnt_of_match);
>> +
>> +static struct platform_driver stm32_lptim_cnt_driver = {
>> +	.probe = stm32_lptim_cnt_probe,
>> +	.driver = {
>> +		.name = "stm32-lptimer-counter",
>> +		.of_match_table = stm32_lptim_cnt_of_match,
>> +	},
>> +};
>> +module_platform_driver(stm32_lptim_cnt_driver);
>> +
>> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
>> +MODULE_ALIAS("platform:stm32-lptimer-counter");
>> +MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
>> +MODULE_LICENSE("GPL v2");
>

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 1/9] dt-bindings: mfd: Add STM32 LPTimer binding
@ 2017-07-10 15:26     ` Rob Herring
  0 siblings, 0 replies; 63+ messages in thread
From: Rob Herring @ 2017-07-10 15:26 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: lee.jones, benjamin.gaignard, jic23, thierry.reding,
	mark.rutland, alexandre.torgue, mcoquelin.stm32,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm

On Fri, Jul 07, 2017 at 06:31:00PM +0200, Fabrice Gasnier wrote:
> Add documentation for STMicroelectronics STM32 Low-Power Timer binding.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> Acked-by: Jonathan Cameron <jic23@kernel.org>
> ---
> Changes in v3:
> - Rob's comment on node name: timer@...
> 
> Changes in v2:
> - Lee's comments: s/Low Power/Low-Power/, remove 0x in example, improve
>   properties descriptions
> ---
>  .../devicetree/bindings/mfd/stm32-lptimer.txt      | 48 ++++++++++++++++++++++
>  1 file changed, 48 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mfd/stm32-lptimer.txt

Acked-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 1/9] dt-bindings: mfd: Add STM32 LPTimer binding
@ 2017-07-10 15:26     ` Rob Herring
  0 siblings, 0 replies; 63+ messages in thread
From: Rob Herring @ 2017-07-10 15:26 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: lee.jones-QSEj5FYQhm4dnm+yROfE0A,
	benjamin.gaignard-QSEj5FYQhm4dnm+yROfE0A,
	jic23-DgEjT+Ai2ygdnm+yROfE0A,
	thierry.reding-Re5JQEeQqe8AvxtiuMwx3w, mark.rutland-5wv7dgnIgG8,
	alexandre.torgue-qxv4g6HH51o,
	mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
	benjamin.gaignard-qxv4g6HH51o, linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-pwm-u79uwXL29TY76Z2rM5mHXA

On Fri, Jul 07, 2017 at 06:31:00PM +0200, Fabrice Gasnier wrote:
> Add documentation for STMicroelectronics STM32 Low-Power Timer binding.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
> Acked-by: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> ---
> Changes in v3:
> - Rob's comment on node name: timer@...
> 
> Changes in v2:
> - Lee's comments: s/Low Power/Low-Power/, remove 0x in example, improve
>   properties descriptions
> ---
>  .../devicetree/bindings/mfd/stm32-lptimer.txt      | 48 ++++++++++++++++++++++
>  1 file changed, 48 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mfd/stm32-lptimer.txt

Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 63+ messages in thread

* [PATCH v3 1/9] dt-bindings: mfd: Add STM32 LPTimer binding
@ 2017-07-10 15:26     ` Rob Herring
  0 siblings, 0 replies; 63+ messages in thread
From: Rob Herring @ 2017-07-10 15:26 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Jul 07, 2017 at 06:31:00PM +0200, Fabrice Gasnier wrote:
> Add documentation for STMicroelectronics STM32 Low-Power Timer binding.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> Acked-by: Jonathan Cameron <jic23@kernel.org>
> ---
> Changes in v3:
> - Rob's comment on node name: timer at ...
> 
> Changes in v2:
> - Lee's comments: s/Low Power/Low-Power/, remove 0x in example, improve
>   properties descriptions
> ---
>  .../devicetree/bindings/mfd/stm32-lptimer.txt      | 48 ++++++++++++++++++++++
>  1 file changed, 48 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mfd/stm32-lptimer.txt

Acked-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 5/9] dt-bindings: iio: Add STM32 LPTimer trigger binding
@ 2017-07-10 15:27     ` Rob Herring
  0 siblings, 0 replies; 63+ messages in thread
From: Rob Herring @ 2017-07-10 15:27 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: lee.jones, benjamin.gaignard, jic23, thierry.reding,
	mark.rutland, alexandre.torgue, mcoquelin.stm32,
	benjamin.gaignard, linux-iio, devicetree, linux-arm-kernel,
	linux-kernel, linux-pwm

On Fri, Jul 07, 2017 at 06:31:04PM +0200, Fabrice Gasnier wrote:
> Add documentation for STMicroelectronics STM32 Low-Power Timer Trigger
> binding.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> Acked-by: Jonathan Cameron <jic23@kernel.org>
> ---
> Changes in v3:
> - Rob's comments: node name timer@..., update reg property
>   description
> 
> Changes in v2:
> - s/Low Power/Low-Power
> - remove leading 0x in example (parent node)
> - improve reg property description
> ---
>  .../bindings/iio/timer/stm32-lptimer-trigger.txt   | 23 ++++++++++++++++++++++
>  1 file changed, 23 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt

Acked-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 5/9] dt-bindings: iio: Add STM32 LPTimer trigger binding
@ 2017-07-10 15:27     ` Rob Herring
  0 siblings, 0 replies; 63+ messages in thread
From: Rob Herring @ 2017-07-10 15:27 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: lee.jones-QSEj5FYQhm4dnm+yROfE0A,
	benjamin.gaignard-QSEj5FYQhm4dnm+yROfE0A,
	jic23-DgEjT+Ai2ygdnm+yROfE0A,
	thierry.reding-Re5JQEeQqe8AvxtiuMwx3w, mark.rutland-5wv7dgnIgG8,
	alexandre.torgue-qxv4g6HH51o,
	mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
	benjamin.gaignard-qxv4g6HH51o, linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-pwm-u79uwXL29TY76Z2rM5mHXA

On Fri, Jul 07, 2017 at 06:31:04PM +0200, Fabrice Gasnier wrote:
> Add documentation for STMicroelectronics STM32 Low-Power Timer Trigger
> binding.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
> Acked-by: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> ---
> Changes in v3:
> - Rob's comments: node name timer@..., update reg property
>   description
> 
> Changes in v2:
> - s/Low Power/Low-Power
> - remove leading 0x in example (parent node)
> - improve reg property description
> ---
>  .../bindings/iio/timer/stm32-lptimer-trigger.txt   | 23 ++++++++++++++++++++++
>  1 file changed, 23 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt

Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

^ permalink raw reply	[flat|nested] 63+ messages in thread

* [PATCH v3 5/9] dt-bindings: iio: Add STM32 LPTimer trigger binding
@ 2017-07-10 15:27     ` Rob Herring
  0 siblings, 0 replies; 63+ messages in thread
From: Rob Herring @ 2017-07-10 15:27 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Jul 07, 2017 at 06:31:04PM +0200, Fabrice Gasnier wrote:
> Add documentation for STMicroelectronics STM32 Low-Power Timer Trigger
> binding.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> Acked-by: Jonathan Cameron <jic23@kernel.org>
> ---
> Changes in v3:
> - Rob's comments: node name timer at ..., update reg property
>   description
> 
> Changes in v2:
> - s/Low Power/Low-Power
> - remove leading 0x in example (parent node)
> - improve reg property description
> ---
>  .../bindings/iio/timer/stm32-lptimer-trigger.txt   | 23 ++++++++++++++++++++++
>  1 file changed, 23 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/timer/stm32-lptimer-trigger.txt

Acked-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 2/9] mfd: Add STM32 LPTimer driver
  2017-07-07 16:31   ` Fabrice Gasnier
@ 2017-07-17 18:33     ` Lee Jones
  -1 siblings, 0 replies; 63+ messages in thread
From: Lee Jones @ 2017-07-17 18:33 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: benjamin.gaignard, jic23, thierry.reding, robh+dt, mark.rutland,
	alexandre.torgue, mcoquelin.stm32, benjamin.gaignard, linux-iio,
	devicetree, linux-arm-kernel, linux-kernel, linux-pwm

On Fri, 07 Jul 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>

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

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

^ permalink raw reply	[flat|nested] 63+ messages in thread

* [PATCH v3 2/9] mfd: Add STM32 LPTimer driver
@ 2017-07-17 18:33     ` Lee Jones
  0 siblings, 0 replies; 63+ messages in thread
From: Lee Jones @ 2017-07-17 18:33 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, 07 Jul 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>

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

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

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 1/9] dt-bindings: mfd: Add STM32 LPTimer binding
  2017-07-07 16:31   ` Fabrice Gasnier
@ 2017-07-17 18:34     ` Lee Jones
  -1 siblings, 0 replies; 63+ messages in thread
From: Lee Jones @ 2017-07-17 18:34 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: benjamin.gaignard, jic23, thierry.reding, robh+dt, mark.rutland,
	alexandre.torgue, mcoquelin.stm32, benjamin.gaignard, linux-iio,
	devicetree, linux-arm-kernel, linux-kernel, linux-pwm

On Fri, 07 Jul 2017, Fabrice Gasnier wrote:

> Add documentation for STMicroelectronics STM32 Low-Power Timer binding.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> Acked-by: Jonathan Cameron <jic23@kernel.org>
> ---
> Changes in v3:
> - Rob's comment on node name: timer@...
> 
> Changes in v2:
> - Lee's comments: s/Low Power/Low-Power/, remove 0x in example, improve
>   properties descriptions
> ---
>  .../devicetree/bindings/mfd/stm32-lptimer.txt      | 48 ++++++++++++++++++++++
>  1 file changed, 48 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mfd/stm32-lptimer.txt

For my own reference:
  Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>

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

^ permalink raw reply	[flat|nested] 63+ messages in thread

* [PATCH v3 1/9] dt-bindings: mfd: Add STM32 LPTimer binding
@ 2017-07-17 18:34     ` Lee Jones
  0 siblings, 0 replies; 63+ messages in thread
From: Lee Jones @ 2017-07-17 18:34 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, 07 Jul 2017, Fabrice Gasnier wrote:

> Add documentation for STMicroelectronics STM32 Low-Power Timer binding.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> Acked-by: Jonathan Cameron <jic23@kernel.org>
> ---
> Changes in v3:
> - Rob's comment on node name: timer at ...
> 
> Changes in v2:
> - Lee's comments: s/Low Power/Low-Power/, remove 0x in example, improve
>   properties descriptions
> ---
>  .../devicetree/bindings/mfd/stm32-lptimer.txt      | 48 ++++++++++++++++++++++
>  1 file changed, 48 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mfd/stm32-lptimer.txt

For my own reference:
  Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>

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

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 4/9] pwm: Add STM32 LPTimer PWM driver
  2017-07-07 16:31   ` Fabrice Gasnier
  (?)
@ 2017-07-26 12:35     ` Fabrice Gasnier
  -1 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-26 12:35 UTC (permalink / raw)
  To: thierry.reding
  Cc: lee.jones, benjamin.gaignard, jic23, robh+dt, mark.rutland,
	alexandre.torgue, mcoquelin.stm32, benjamin.gaignard, linux-iio,
	devicetree, linux-arm-kernel, linux-kernel, linux-pwm

On 07/07/2017 06:31 PM, Fabrice Gasnier wrote:
> Add support for single PWM channel on Low-Power Timer, that can be
> found on some STM32 platforms.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> ---
> Changes in v3:
> - remove prescalers[] array, use power-of-2 presc directly
> - Update following Thierry's comments:
> - fix issue using FIELD_GET() macro
> - Add get_state() callback
> - remove some checks in probe
> - slight rework 'reenable' flag
> - use more common method to disable pwm in remove()

Hi Thierry,

Gentle ping for PWM driver review since I did changes in v3.
Please advise.

Best Regards,
Fabrice
> 
> Changes in v2:
> - s/Low Power/Low-Power
> - update few comment lines
> ---
>  drivers/pwm/Kconfig        |  10 ++
>  drivers/pwm/Makefile       |   1 +
>  drivers/pwm/pwm-stm32-lp.c | 246 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 257 insertions(+)
>  create mode 100644 drivers/pwm/pwm-stm32-lp.c
> 
> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> index 313c107..7cb982b 100644
> --- a/drivers/pwm/Kconfig
> +++ b/drivers/pwm/Kconfig
> @@ -417,6 +417,16 @@ config PWM_STM32
>  	  To compile this driver as a module, choose M here: the module
>  	  will be called pwm-stm32.
>  
> +config PWM_STM32_LP
> +	tristate "STMicroelectronics STM32 PWM LP"
> +	depends on MFD_STM32_LPTIMER || COMPILE_TEST
> +	help
> +	  Generic PWM framework driver for STMicroelectronics STM32 SoCs
> +	  with Low-Power Timer (LPTIM).
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called pwm-stm32-lp.
> +
>  config PWM_STMPE
>  	bool "STMPE expander PWM export"
>  	depends on MFD_STMPE
> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
> index 93da1f7..a3a4bee 100644
> --- a/drivers/pwm/Makefile
> +++ b/drivers/pwm/Makefile
> @@ -40,6 +40,7 @@ obj-$(CONFIG_PWM_SAMSUNG)	+= pwm-samsung.o
>  obj-$(CONFIG_PWM_SPEAR)		+= pwm-spear.o
>  obj-$(CONFIG_PWM_STI)		+= pwm-sti.o
>  obj-$(CONFIG_PWM_STM32)		+= pwm-stm32.o
> +obj-$(CONFIG_PWM_STM32_LP)	+= pwm-stm32-lp.o
>  obj-$(CONFIG_PWM_STMPE)		+= pwm-stmpe.o
>  obj-$(CONFIG_PWM_SUN4I)		+= pwm-sun4i.o
>  obj-$(CONFIG_PWM_TEGRA)		+= pwm-tegra.o
> diff --git a/drivers/pwm/pwm-stm32-lp.c b/drivers/pwm/pwm-stm32-lp.c
> new file mode 100644
> index 0000000..9793b29
> --- /dev/null
> +++ b/drivers/pwm/pwm-stm32-lp.c
> @@ -0,0 +1,246 @@
> +/*
> + * STM32 Low-Power Timer PWM driver
> + *
> + * Copyright (C) STMicroelectronics 2017
> + *
> + * Author: Gerald Baeza <gerald.baeza@st.com>
> + *
> + * License terms: GNU General Public License (GPL), version 2
> + *
> + * Inspired by Gerald Baeza's pwm-stm32 driver
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/mfd/stm32-lptimer.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/pwm.h>
> +
> +struct stm32_pwm_lp {
> +	struct pwm_chip chip;
> +	struct clk *clk;
> +	struct regmap *regmap;
> +};
> +
> +static inline struct stm32_pwm_lp *to_stm32_pwm_lp(struct pwm_chip *chip)
> +{
> +	return container_of(chip, struct stm32_pwm_lp, chip);
> +}
> +
> +/* STM32 Low-Power Timer is preceded by a configurable power-of-2 prescaler */
> +#define STM32_LPTIM_MAX_PRESCALER	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, presc = 0;
> +	bool reenable;
> +	int ret;
> +
> +	pwm_get_state(pwm, &cstate);
> +	reenable = !cstate.enabled;
> +
> +	if (!state->enabled) {
> +		if (cstate.enabled) {
> +			/* Disable LP timer */
> +			ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
> +			if (ret)
> +				return ret;
> +			/* disable clock to PWM counter */
> +			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 ((1 << presc) > STM32_LPTIM_MAX_PRESCALER) {
> +			dev_err(priv->chip.dev, "max prescaler exceeded\n");
> +			return -EINVAL;
> +		}
> +		div = prd >> presc;
> +	}
> +	prd = div;
> +
> +	/* Calculate the duty cycle */
> +	dty = prd * state->duty_cycle;
> +	do_div(dty, state->period);
> +
> +	if (!cstate.enabled) {
> +		/* enable clock to drive PWM counter */
> +		ret = clk_enable(priv->clk);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = regmap_read(priv->regmap, STM32_LPTIM_CFGR, &cfgr);
> +	if (ret)
> +		goto err;
> +
> +	if ((FIELD_GET(STM32_LPTIM_PRESC, cfgr) != presc) ||
> +	    (FIELD_GET(STM32_LPTIM_WAVPOL, cfgr) != state->polarity)) {
> +		val = FIELD_PREP(STM32_LPTIM_PRESC, presc);
> +		val |= FIELD_PREP(STM32_LPTIM_WAVPOL, state->polarity);
> +		mask = STM32_LPTIM_PRESC | STM32_LPTIM_WAVPOL;
> +
> +		/* Must disable LP timer to modify CFGR */
> +		reenable = true;
> +		ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
> +		if (ret)
> +			goto err;
> +
> +		ret = regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask,
> +					 val);
> +		if (ret)
> +			goto err;
> +	}
> +
> +	if (reenable) {
> +		/* Must (re)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 (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 void stm32_pwm_lp_get_state(struct pwm_chip *chip,
> +				   struct pwm_device *pwm,
> +				   struct pwm_state *state)
> +{
> +	struct stm32_pwm_lp *priv = to_stm32_pwm_lp(chip);
> +	unsigned long rate = clk_get_rate(priv->clk);
> +	u32 val, presc, prd;
> +	u64 tmp;
> +
> +	regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
> +	state->enabled = !!FIELD_GET(STM32_LPTIM_ENABLE, val);
> +	/* Keep PWM counter clock refcount in sync with PWM initial state */
> +	if (state->enabled)
> +		clk_enable(priv->clk);
> +
> +	regmap_read(priv->regmap, STM32_LPTIM_CFGR, &val);
> +	presc = FIELD_GET(STM32_LPTIM_PRESC, val);
> +	state->polarity = FIELD_GET(STM32_LPTIM_WAVPOL, val);
> +
> +	regmap_read(priv->regmap, STM32_LPTIM_ARR, &prd);
> +	tmp = prd + 1;
> +	tmp = (tmp << presc) * NSEC_PER_SEC;
> +	state->period = DIV_ROUND_CLOSEST_ULL(tmp, rate);
> +
> +	regmap_read(priv->regmap, STM32_LPTIM_CMP, &val);
> +	tmp = prd - val;
> +	tmp = (tmp << presc) * NSEC_PER_SEC;
> +	state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, rate);
> +}
> +
> +static const struct pwm_ops stm32_pwm_lp_ops = {
> +	.owner = THIS_MODULE,
> +	.apply = stm32_pwm_lp_apply,
> +	.get_state = stm32_pwm_lp_get_state,
> +};
> +
> +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;
> +
> +	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->regmap = ddata->regmap;
> +	priv->clk = ddata->clk;
> +	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);
> +	unsigned int i;
> +
> +	for (i = 0; i < priv->chip.npwm; i++)
> +		if (pwm_is_enabled(&priv->chip.pwms[i]))
> +			pwm_disable(&priv->chip.pwms[i]);
> +
> +	return pwmchip_remove(&priv->chip);
> +}
> +
> +static const struct of_device_id stm32_pwm_lp_of_match[] = {
> +	{ .compatible = "st,stm32-pwm-lp", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, stm32_pwm_lp_of_match);
> +
> +static struct platform_driver stm32_pwm_lp_driver = {
> +	.probe	= stm32_pwm_lp_probe,
> +	.remove	= stm32_pwm_lp_remove,
> +	.driver	= {
> +		.name = "stm32-pwm-lp",
> +		.of_match_table = of_match_ptr(stm32_pwm_lp_of_match),
> +	},
> +};
> +module_platform_driver(stm32_pwm_lp_driver);
> +
> +MODULE_ALIAS("platform:stm32-pwm-lp");
> +MODULE_DESCRIPTION("STMicroelectronics STM32 PWM LP driver");
> +MODULE_LICENSE("GPL v2");
> 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 4/9] pwm: Add STM32 LPTimer PWM driver
@ 2017-07-26 12:35     ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-26 12:35 UTC (permalink / raw)
  To: thierry.reding
  Cc: mark.rutland, mcoquelin.stm32, alexandre.torgue, devicetree,
	linux-iio, linux-kernel, linux-pwm, robh+dt, linux-arm-kernel,
	benjamin.gaignard, lee.jones, jic23, benjamin.gaignard

On 07/07/2017 06:31 PM, Fabrice Gasnier wrote:
> Add support for single PWM channel on Low-Power Timer, that can be
> found on some STM32 platforms.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> ---
> Changes in v3:
> - remove prescalers[] array, use power-of-2 presc directly
> - Update following Thierry's comments:
> - fix issue using FIELD_GET() macro
> - Add get_state() callback
> - remove some checks in probe
> - slight rework 'reenable' flag
> - use more common method to disable pwm in remove()

Hi Thierry,

Gentle ping for PWM driver review since I did changes in v3.
Please advise.

Best Regards,
Fabrice
> 
> Changes in v2:
> - s/Low Power/Low-Power
> - update few comment lines
> ---
>  drivers/pwm/Kconfig        |  10 ++
>  drivers/pwm/Makefile       |   1 +
>  drivers/pwm/pwm-stm32-lp.c | 246 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 257 insertions(+)
>  create mode 100644 drivers/pwm/pwm-stm32-lp.c
> 
> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> index 313c107..7cb982b 100644
> --- a/drivers/pwm/Kconfig
> +++ b/drivers/pwm/Kconfig
> @@ -417,6 +417,16 @@ config PWM_STM32
>  	  To compile this driver as a module, choose M here: the module
>  	  will be called pwm-stm32.
>  
> +config PWM_STM32_LP
> +	tristate "STMicroelectronics STM32 PWM LP"
> +	depends on MFD_STM32_LPTIMER || COMPILE_TEST
> +	help
> +	  Generic PWM framework driver for STMicroelectronics STM32 SoCs
> +	  with Low-Power Timer (LPTIM).
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called pwm-stm32-lp.
> +
>  config PWM_STMPE
>  	bool "STMPE expander PWM export"
>  	depends on MFD_STMPE
> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
> index 93da1f7..a3a4bee 100644
> --- a/drivers/pwm/Makefile
> +++ b/drivers/pwm/Makefile
> @@ -40,6 +40,7 @@ obj-$(CONFIG_PWM_SAMSUNG)	+= pwm-samsung.o
>  obj-$(CONFIG_PWM_SPEAR)		+= pwm-spear.o
>  obj-$(CONFIG_PWM_STI)		+= pwm-sti.o
>  obj-$(CONFIG_PWM_STM32)		+= pwm-stm32.o
> +obj-$(CONFIG_PWM_STM32_LP)	+= pwm-stm32-lp.o
>  obj-$(CONFIG_PWM_STMPE)		+= pwm-stmpe.o
>  obj-$(CONFIG_PWM_SUN4I)		+= pwm-sun4i.o
>  obj-$(CONFIG_PWM_TEGRA)		+= pwm-tegra.o
> diff --git a/drivers/pwm/pwm-stm32-lp.c b/drivers/pwm/pwm-stm32-lp.c
> new file mode 100644
> index 0000000..9793b29
> --- /dev/null
> +++ b/drivers/pwm/pwm-stm32-lp.c
> @@ -0,0 +1,246 @@
> +/*
> + * STM32 Low-Power Timer PWM driver
> + *
> + * Copyright (C) STMicroelectronics 2017
> + *
> + * Author: Gerald Baeza <gerald.baeza@st.com>
> + *
> + * License terms: GNU General Public License (GPL), version 2
> + *
> + * Inspired by Gerald Baeza's pwm-stm32 driver
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/mfd/stm32-lptimer.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/pwm.h>
> +
> +struct stm32_pwm_lp {
> +	struct pwm_chip chip;
> +	struct clk *clk;
> +	struct regmap *regmap;
> +};
> +
> +static inline struct stm32_pwm_lp *to_stm32_pwm_lp(struct pwm_chip *chip)
> +{
> +	return container_of(chip, struct stm32_pwm_lp, chip);
> +}
> +
> +/* STM32 Low-Power Timer is preceded by a configurable power-of-2 prescaler */
> +#define STM32_LPTIM_MAX_PRESCALER	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, presc = 0;
> +	bool reenable;
> +	int ret;
> +
> +	pwm_get_state(pwm, &cstate);
> +	reenable = !cstate.enabled;
> +
> +	if (!state->enabled) {
> +		if (cstate.enabled) {
> +			/* Disable LP timer */
> +			ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
> +			if (ret)
> +				return ret;
> +			/* disable clock to PWM counter */
> +			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 ((1 << presc) > STM32_LPTIM_MAX_PRESCALER) {
> +			dev_err(priv->chip.dev, "max prescaler exceeded\n");
> +			return -EINVAL;
> +		}
> +		div = prd >> presc;
> +	}
> +	prd = div;
> +
> +	/* Calculate the duty cycle */
> +	dty = prd * state->duty_cycle;
> +	do_div(dty, state->period);
> +
> +	if (!cstate.enabled) {
> +		/* enable clock to drive PWM counter */
> +		ret = clk_enable(priv->clk);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = regmap_read(priv->regmap, STM32_LPTIM_CFGR, &cfgr);
> +	if (ret)
> +		goto err;
> +
> +	if ((FIELD_GET(STM32_LPTIM_PRESC, cfgr) != presc) ||
> +	    (FIELD_GET(STM32_LPTIM_WAVPOL, cfgr) != state->polarity)) {
> +		val = FIELD_PREP(STM32_LPTIM_PRESC, presc);
> +		val |= FIELD_PREP(STM32_LPTIM_WAVPOL, state->polarity);
> +		mask = STM32_LPTIM_PRESC | STM32_LPTIM_WAVPOL;
> +
> +		/* Must disable LP timer to modify CFGR */
> +		reenable = true;
> +		ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
> +		if (ret)
> +			goto err;
> +
> +		ret = regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask,
> +					 val);
> +		if (ret)
> +			goto err;
> +	}
> +
> +	if (reenable) {
> +		/* Must (re)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 (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 void stm32_pwm_lp_get_state(struct pwm_chip *chip,
> +				   struct pwm_device *pwm,
> +				   struct pwm_state *state)
> +{
> +	struct stm32_pwm_lp *priv = to_stm32_pwm_lp(chip);
> +	unsigned long rate = clk_get_rate(priv->clk);
> +	u32 val, presc, prd;
> +	u64 tmp;
> +
> +	regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
> +	state->enabled = !!FIELD_GET(STM32_LPTIM_ENABLE, val);
> +	/* Keep PWM counter clock refcount in sync with PWM initial state */
> +	if (state->enabled)
> +		clk_enable(priv->clk);
> +
> +	regmap_read(priv->regmap, STM32_LPTIM_CFGR, &val);
> +	presc = FIELD_GET(STM32_LPTIM_PRESC, val);
> +	state->polarity = FIELD_GET(STM32_LPTIM_WAVPOL, val);
> +
> +	regmap_read(priv->regmap, STM32_LPTIM_ARR, &prd);
> +	tmp = prd + 1;
> +	tmp = (tmp << presc) * NSEC_PER_SEC;
> +	state->period = DIV_ROUND_CLOSEST_ULL(tmp, rate);
> +
> +	regmap_read(priv->regmap, STM32_LPTIM_CMP, &val);
> +	tmp = prd - val;
> +	tmp = (tmp << presc) * NSEC_PER_SEC;
> +	state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, rate);
> +}
> +
> +static const struct pwm_ops stm32_pwm_lp_ops = {
> +	.owner = THIS_MODULE,
> +	.apply = stm32_pwm_lp_apply,
> +	.get_state = stm32_pwm_lp_get_state,
> +};
> +
> +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;
> +
> +	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->regmap = ddata->regmap;
> +	priv->clk = ddata->clk;
> +	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);
> +	unsigned int i;
> +
> +	for (i = 0; i < priv->chip.npwm; i++)
> +		if (pwm_is_enabled(&priv->chip.pwms[i]))
> +			pwm_disable(&priv->chip.pwms[i]);
> +
> +	return pwmchip_remove(&priv->chip);
> +}
> +
> +static const struct of_device_id stm32_pwm_lp_of_match[] = {
> +	{ .compatible = "st,stm32-pwm-lp", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, stm32_pwm_lp_of_match);
> +
> +static struct platform_driver stm32_pwm_lp_driver = {
> +	.probe	= stm32_pwm_lp_probe,
> +	.remove	= stm32_pwm_lp_remove,
> +	.driver	= {
> +		.name = "stm32-pwm-lp",
> +		.of_match_table = of_match_ptr(stm32_pwm_lp_of_match),
> +	},
> +};
> +module_platform_driver(stm32_pwm_lp_driver);
> +
> +MODULE_ALIAS("platform:stm32-pwm-lp");
> +MODULE_DESCRIPTION("STMicroelectronics STM32 PWM LP driver");
> +MODULE_LICENSE("GPL v2");
> 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* [PATCH v3 4/9] pwm: Add STM32 LPTimer PWM driver
@ 2017-07-26 12:35     ` Fabrice Gasnier
  0 siblings, 0 replies; 63+ messages in thread
From: Fabrice Gasnier @ 2017-07-26 12:35 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/07/2017 06:31 PM, Fabrice Gasnier wrote:
> Add support for single PWM channel on Low-Power Timer, that can be
> found on some STM32 platforms.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> ---
> Changes in v3:
> - remove prescalers[] array, use power-of-2 presc directly
> - Update following Thierry's comments:
> - fix issue using FIELD_GET() macro
> - Add get_state() callback
> - remove some checks in probe
> - slight rework 'reenable' flag
> - use more common method to disable pwm in remove()

Hi Thierry,

Gentle ping for PWM driver review since I did changes in v3.
Please advise.

Best Regards,
Fabrice
> 
> Changes in v2:
> - s/Low Power/Low-Power
> - update few comment lines
> ---
>  drivers/pwm/Kconfig        |  10 ++
>  drivers/pwm/Makefile       |   1 +
>  drivers/pwm/pwm-stm32-lp.c | 246 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 257 insertions(+)
>  create mode 100644 drivers/pwm/pwm-stm32-lp.c
> 
> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> index 313c107..7cb982b 100644
> --- a/drivers/pwm/Kconfig
> +++ b/drivers/pwm/Kconfig
> @@ -417,6 +417,16 @@ config PWM_STM32
>  	  To compile this driver as a module, choose M here: the module
>  	  will be called pwm-stm32.
>  
> +config PWM_STM32_LP
> +	tristate "STMicroelectronics STM32 PWM LP"
> +	depends on MFD_STM32_LPTIMER || COMPILE_TEST
> +	help
> +	  Generic PWM framework driver for STMicroelectronics STM32 SoCs
> +	  with Low-Power Timer (LPTIM).
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called pwm-stm32-lp.
> +
>  config PWM_STMPE
>  	bool "STMPE expander PWM export"
>  	depends on MFD_STMPE
> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
> index 93da1f7..a3a4bee 100644
> --- a/drivers/pwm/Makefile
> +++ b/drivers/pwm/Makefile
> @@ -40,6 +40,7 @@ obj-$(CONFIG_PWM_SAMSUNG)	+= pwm-samsung.o
>  obj-$(CONFIG_PWM_SPEAR)		+= pwm-spear.o
>  obj-$(CONFIG_PWM_STI)		+= pwm-sti.o
>  obj-$(CONFIG_PWM_STM32)		+= pwm-stm32.o
> +obj-$(CONFIG_PWM_STM32_LP)	+= pwm-stm32-lp.o
>  obj-$(CONFIG_PWM_STMPE)		+= pwm-stmpe.o
>  obj-$(CONFIG_PWM_SUN4I)		+= pwm-sun4i.o
>  obj-$(CONFIG_PWM_TEGRA)		+= pwm-tegra.o
> diff --git a/drivers/pwm/pwm-stm32-lp.c b/drivers/pwm/pwm-stm32-lp.c
> new file mode 100644
> index 0000000..9793b29
> --- /dev/null
> +++ b/drivers/pwm/pwm-stm32-lp.c
> @@ -0,0 +1,246 @@
> +/*
> + * STM32 Low-Power Timer PWM driver
> + *
> + * Copyright (C) STMicroelectronics 2017
> + *
> + * Author: Gerald Baeza <gerald.baeza@st.com>
> + *
> + * License terms: GNU General Public License (GPL), version 2
> + *
> + * Inspired by Gerald Baeza's pwm-stm32 driver
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/mfd/stm32-lptimer.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/pwm.h>
> +
> +struct stm32_pwm_lp {
> +	struct pwm_chip chip;
> +	struct clk *clk;
> +	struct regmap *regmap;
> +};
> +
> +static inline struct stm32_pwm_lp *to_stm32_pwm_lp(struct pwm_chip *chip)
> +{
> +	return container_of(chip, struct stm32_pwm_lp, chip);
> +}
> +
> +/* STM32 Low-Power Timer is preceded by a configurable power-of-2 prescaler */
> +#define STM32_LPTIM_MAX_PRESCALER	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, presc = 0;
> +	bool reenable;
> +	int ret;
> +
> +	pwm_get_state(pwm, &cstate);
> +	reenable = !cstate.enabled;
> +
> +	if (!state->enabled) {
> +		if (cstate.enabled) {
> +			/* Disable LP timer */
> +			ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
> +			if (ret)
> +				return ret;
> +			/* disable clock to PWM counter */
> +			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 ((1 << presc) > STM32_LPTIM_MAX_PRESCALER) {
> +			dev_err(priv->chip.dev, "max prescaler exceeded\n");
> +			return -EINVAL;
> +		}
> +		div = prd >> presc;
> +	}
> +	prd = div;
> +
> +	/* Calculate the duty cycle */
> +	dty = prd * state->duty_cycle;
> +	do_div(dty, state->period);
> +
> +	if (!cstate.enabled) {
> +		/* enable clock to drive PWM counter */
> +		ret = clk_enable(priv->clk);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = regmap_read(priv->regmap, STM32_LPTIM_CFGR, &cfgr);
> +	if (ret)
> +		goto err;
> +
> +	if ((FIELD_GET(STM32_LPTIM_PRESC, cfgr) != presc) ||
> +	    (FIELD_GET(STM32_LPTIM_WAVPOL, cfgr) != state->polarity)) {
> +		val = FIELD_PREP(STM32_LPTIM_PRESC, presc);
> +		val |= FIELD_PREP(STM32_LPTIM_WAVPOL, state->polarity);
> +		mask = STM32_LPTIM_PRESC | STM32_LPTIM_WAVPOL;
> +
> +		/* Must disable LP timer to modify CFGR */
> +		reenable = true;
> +		ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
> +		if (ret)
> +			goto err;
> +
> +		ret = regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask,
> +					 val);
> +		if (ret)
> +			goto err;
> +	}
> +
> +	if (reenable) {
> +		/* Must (re)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 (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 void stm32_pwm_lp_get_state(struct pwm_chip *chip,
> +				   struct pwm_device *pwm,
> +				   struct pwm_state *state)
> +{
> +	struct stm32_pwm_lp *priv = to_stm32_pwm_lp(chip);
> +	unsigned long rate = clk_get_rate(priv->clk);
> +	u32 val, presc, prd;
> +	u64 tmp;
> +
> +	regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
> +	state->enabled = !!FIELD_GET(STM32_LPTIM_ENABLE, val);
> +	/* Keep PWM counter clock refcount in sync with PWM initial state */
> +	if (state->enabled)
> +		clk_enable(priv->clk);
> +
> +	regmap_read(priv->regmap, STM32_LPTIM_CFGR, &val);
> +	presc = FIELD_GET(STM32_LPTIM_PRESC, val);
> +	state->polarity = FIELD_GET(STM32_LPTIM_WAVPOL, val);
> +
> +	regmap_read(priv->regmap, STM32_LPTIM_ARR, &prd);
> +	tmp = prd + 1;
> +	tmp = (tmp << presc) * NSEC_PER_SEC;
> +	state->period = DIV_ROUND_CLOSEST_ULL(tmp, rate);
> +
> +	regmap_read(priv->regmap, STM32_LPTIM_CMP, &val);
> +	tmp = prd - val;
> +	tmp = (tmp << presc) * NSEC_PER_SEC;
> +	state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, rate);
> +}
> +
> +static const struct pwm_ops stm32_pwm_lp_ops = {
> +	.owner = THIS_MODULE,
> +	.apply = stm32_pwm_lp_apply,
> +	.get_state = stm32_pwm_lp_get_state,
> +};
> +
> +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;
> +
> +	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->regmap = ddata->regmap;
> +	priv->clk = ddata->clk;
> +	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);
> +	unsigned int i;
> +
> +	for (i = 0; i < priv->chip.npwm; i++)
> +		if (pwm_is_enabled(&priv->chip.pwms[i]))
> +			pwm_disable(&priv->chip.pwms[i]);
> +
> +	return pwmchip_remove(&priv->chip);
> +}
> +
> +static const struct of_device_id stm32_pwm_lp_of_match[] = {
> +	{ .compatible = "st,stm32-pwm-lp", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, stm32_pwm_lp_of_match);
> +
> +static struct platform_driver stm32_pwm_lp_driver = {
> +	.probe	= stm32_pwm_lp_probe,
> +	.remove	= stm32_pwm_lp_remove,
> +	.driver	= {
> +		.name = "stm32-pwm-lp",
> +		.of_match_table = of_match_ptr(stm32_pwm_lp_of_match),
> +	},
> +};
> +module_platform_driver(stm32_pwm_lp_driver);
> +
> +MODULE_ALIAS("platform:stm32-pwm-lp");
> +MODULE_DESCRIPTION("STMicroelectronics STM32 PWM LP driver");
> +MODULE_LICENSE("GPL v2");
> 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 4/9] pwm: Add STM32 LPTimer PWM driver
@ 2017-08-21  7:01     ` Thierry Reding
  0 siblings, 0 replies; 63+ messages in thread
From: Thierry Reding @ 2017-08-21  7:01 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: lee.jones, benjamin.gaignard, jic23, robh+dt, mark.rutland,
	alexandre.torgue, mcoquelin.stm32, benjamin.gaignard, linux-iio,
	devicetree, linux-arm-kernel, linux-kernel, linux-pwm

[-- Attachment #1: Type: text/plain, Size: 1017 bytes --]

On Fri, Jul 07, 2017 at 06:31:03PM +0200, Fabrice Gasnier wrote:
> Add support for single PWM channel on Low-Power Timer, that can be
> found on some STM32 platforms.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> ---
> Changes in v3:
> - remove prescalers[] array, use power-of-2 presc directly
> - Update following Thierry's comments:
> - fix issue using FIELD_GET() macro
> - Add get_state() callback
> - remove some checks in probe
> - slight rework 'reenable' flag
> - use more common method to disable pwm in remove()
> 
> Changes in v2:
> - s/Low Power/Low-Power
> - update few comment lines
> ---
>  drivers/pwm/Kconfig        |  10 ++
>  drivers/pwm/Makefile       |   1 +
>  drivers/pwm/pwm-stm32-lp.c | 246 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 257 insertions(+)
>  create mode 100644 drivers/pwm/pwm-stm32-lp.c

Sorry, this fell through the cracks: changes in v3 look good to me:

Acked-by: Thierry Reding <thierry.reding@gmail.com>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 4/9] pwm: Add STM32 LPTimer PWM driver
@ 2017-08-21  7:01     ` Thierry Reding
  0 siblings, 0 replies; 63+ messages in thread
From: Thierry Reding @ 2017-08-21  7:01 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: lee.jones-QSEj5FYQhm4dnm+yROfE0A,
	benjamin.gaignard-QSEj5FYQhm4dnm+yROfE0A,
	jic23-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, alexandre.torgue-qxv4g6HH51o,
	mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
	benjamin.gaignard-qxv4g6HH51o, linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-pwm-u79uwXL29TY76Z2rM5mHXA

[-- Attachment #1: Type: text/plain, Size: 1069 bytes --]

On Fri, Jul 07, 2017 at 06:31:03PM +0200, Fabrice Gasnier wrote:
> Add support for single PWM channel on Low-Power Timer, that can be
> found on some STM32 platforms.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
> ---
> Changes in v3:
> - remove prescalers[] array, use power-of-2 presc directly
> - Update following Thierry's comments:
> - fix issue using FIELD_GET() macro
> - Add get_state() callback
> - remove some checks in probe
> - slight rework 'reenable' flag
> - use more common method to disable pwm in remove()
> 
> Changes in v2:
> - s/Low Power/Low-Power
> - update few comment lines
> ---
>  drivers/pwm/Kconfig        |  10 ++
>  drivers/pwm/Makefile       |   1 +
>  drivers/pwm/pwm-stm32-lp.c | 246 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 257 insertions(+)
>  create mode 100644 drivers/pwm/pwm-stm32-lp.c

Sorry, this fell through the cracks: changes in v3 look good to me:

Acked-by: Thierry Reding <thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* [PATCH v3 4/9] pwm: Add STM32 LPTimer PWM driver
@ 2017-08-21  7:01     ` Thierry Reding
  0 siblings, 0 replies; 63+ messages in thread
From: Thierry Reding @ 2017-08-21  7:01 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Jul 07, 2017 at 06:31:03PM +0200, Fabrice Gasnier wrote:
> Add support for single PWM channel on Low-Power Timer, that can be
> found on some STM32 platforms.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> ---
> Changes in v3:
> - remove prescalers[] array, use power-of-2 presc directly
> - Update following Thierry's comments:
> - fix issue using FIELD_GET() macro
> - Add get_state() callback
> - remove some checks in probe
> - slight rework 'reenable' flag
> - use more common method to disable pwm in remove()
> 
> Changes in v2:
> - s/Low Power/Low-Power
> - update few comment lines
> ---
>  drivers/pwm/Kconfig        |  10 ++
>  drivers/pwm/Makefile       |   1 +
>  drivers/pwm/pwm-stm32-lp.c | 246 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 257 insertions(+)
>  create mode 100644 drivers/pwm/pwm-stm32-lp.c

Sorry, this fell through the cracks: changes in v3 look good to me:

Acked-by: Thierry Reding <thierry.reding@gmail.com>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170821/6dbb14ed/attachment.sig>

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 0/9] Add STM32 LPTimer: PWM, trigger and counter
@ 2017-08-21  7:24   ` Lee Jones
  0 siblings, 0 replies; 63+ messages in thread
From: Lee Jones @ 2017-08-21  7:24 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: benjamin.gaignard, jic23, thierry.reding, robh+dt, mark.rutland,
	alexandre.torgue, mcoquelin.stm32, benjamin.gaignard, linux-iio,
	devicetree, linux-arm-kernel, linux-kernel, linux-pwm

On Fri, 07 Jul 2017, Fabrice Gasnier wrote:

> This series adds support for Low-Power Timer that can be found on
> some STM32 devices.
> 
> STM32 LPTimer (LPTIM) is a 16-bit timer that provides several
> functionalities. This series adds support for following features:
> - PWM output (with programmable prescaler, configurable polarity)
> - Trigger source for STM32 ADC or DAC (LPTIM_OUT)
> - Quadrature encoder and counter
> 
> The MFD core is used to manage common resources (clock, register map)
> and to detect encoder feature. "stm32_lptimer" structure is provided
> to its sub-nodes to share those information:
> - PWM driver is used to implement single PWM channel
> - IIO trigger
> - IIO quadrature encoder and counter
> 
> ---
> Changes in v3:
> - I sent this v3 with various changes, but still need to discuss ABI
>   for counter driver (no change on this in v3).
> - Remarks for Rob on dt-bindings
> - Add validate_device in Trigger driver
> - Update PWM driver after Thierry's comments
> - Additional patch for STM32 ADC trigger

Looks like you have everything you need.

Please apply all the Acks you've collected and [RESEND].

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

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v3 0/9] Add STM32 LPTimer: PWM, trigger and counter
@ 2017-08-21  7:24   ` Lee Jones
  0 siblings, 0 replies; 63+ messages in thread
From: Lee Jones @ 2017-08-21  7:24 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: benjamin.gaignard-QSEj5FYQhm4dnm+yROfE0A,
	jic23-DgEjT+Ai2ygdnm+yROfE0A,
	thierry.reding-Re5JQEeQqe8AvxtiuMwx3w,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	alexandre.torgue-qxv4g6HH51o,
	mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
	benjamin.gaignard-qxv4g6HH51o, linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-pwm-u79uwXL29TY76Z2rM5mHXA

On Fri, 07 Jul 2017, Fabrice Gasnier wrote:

> This series adds support for Low-Power Timer that can be found on
> some STM32 devices.
> 
> STM32 LPTimer (LPTIM) is a 16-bit timer that provides several
> functionalities. This series adds support for following features:
> - PWM output (with programmable prescaler, configurable polarity)
> - Trigger source for STM32 ADC or DAC (LPTIM_OUT)
> - Quadrature encoder and counter
> 
> The MFD core is used to manage common resources (clock, register map)
> and to detect encoder feature. "stm32_lptimer" structure is provided
> to its sub-nodes to share those information:
> - PWM driver is used to implement single PWM channel
> - IIO trigger
> - IIO quadrature encoder and counter
> 
> ---
> Changes in v3:
> - I sent this v3 with various changes, but still need to discuss ABI
>   for counter driver (no change on this in v3).
> - Remarks for Rob on dt-bindings
> - Add validate_device in Trigger driver
> - Update PWM driver after Thierry's comments
> - Additional patch for STM32 ADC trigger

Looks like you have everything you need.

Please apply all the Acks you've collected and [RESEND].

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

^ permalink raw reply	[flat|nested] 63+ messages in thread

* [PATCH v3 0/9] Add STM32 LPTimer: PWM, trigger and counter
@ 2017-08-21  7:24   ` Lee Jones
  0 siblings, 0 replies; 63+ messages in thread
From: Lee Jones @ 2017-08-21  7:24 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, 07 Jul 2017, Fabrice Gasnier wrote:

> This series adds support for Low-Power Timer that can be found on
> some STM32 devices.
> 
> STM32 LPTimer (LPTIM) is a 16-bit timer that provides several
> functionalities. This series adds support for following features:
> - PWM output (with programmable prescaler, configurable polarity)
> - Trigger source for STM32 ADC or DAC (LPTIM_OUT)
> - Quadrature encoder and counter
> 
> The MFD core is used to manage common resources (clock, register map)
> and to detect encoder feature. "stm32_lptimer" structure is provided
> to its sub-nodes to share those information:
> - PWM driver is used to implement single PWM channel
> - IIO trigger
> - IIO quadrature encoder and counter
> 
> ---
> Changes in v3:
> - I sent this v3 with various changes, but still need to discuss ABI
>   for counter driver (no change on this in v3).
> - Remarks for Rob on dt-bindings
> - Add validate_device in Trigger driver
> - Update PWM driver after Thierry's comments
> - Additional patch for STM32 ADC trigger

Looks like you have everything you need.

Please apply all the Acks you've collected and [RESEND].

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

^ permalink raw reply	[flat|nested] 63+ messages in thread

end of thread, other threads:[~2017-08-21  7:24 UTC | newest]

Thread overview: 63+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-07-07 16:30 [PATCH v3 0/9] Add STM32 LPTimer: PWM, trigger and counter Fabrice Gasnier
2017-07-07 16:30 ` Fabrice Gasnier
2017-07-07 16:30 ` Fabrice Gasnier
2017-07-07 16:31 ` [PATCH v3 1/9] dt-bindings: mfd: Add STM32 LPTimer binding Fabrice Gasnier
2017-07-07 16:31   ` Fabrice Gasnier
2017-07-07 16:31   ` Fabrice Gasnier
2017-07-10 15:26   ` Rob Herring
2017-07-10 15:26     ` Rob Herring
2017-07-10 15:26     ` Rob Herring
2017-07-17 18:34   ` Lee Jones
2017-07-17 18:34     ` Lee Jones
2017-07-07 16:31 ` [PATCH v3 2/9] mfd: Add STM32 LPTimer driver Fabrice Gasnier
2017-07-07 16:31   ` Fabrice Gasnier
2017-07-07 16:31   ` Fabrice Gasnier
2017-07-17 18:33   ` Lee Jones
2017-07-17 18:33     ` Lee Jones
2017-07-07 16:31 ` [PATCH v3 3/9] dt-bindings: pwm: Add STM32 LPTimer PWM binding Fabrice Gasnier
2017-07-07 16:31   ` Fabrice Gasnier
2017-07-07 16:31   ` Fabrice Gasnier
2017-07-07 16:31 ` [PATCH v3 4/9] pwm: Add STM32 LPTimer PWM driver Fabrice Gasnier
2017-07-07 16:31   ` Fabrice Gasnier
2017-07-07 16:31   ` Fabrice Gasnier
2017-07-26 12:35   ` Fabrice Gasnier
2017-07-26 12:35     ` Fabrice Gasnier
2017-07-26 12:35     ` Fabrice Gasnier
2017-08-21  7:01   ` Thierry Reding
2017-08-21  7:01     ` Thierry Reding
2017-08-21  7:01     ` Thierry Reding
2017-07-07 16:31 ` [PATCH v3 5/9] dt-bindings: iio: Add STM32 LPTimer trigger binding Fabrice Gasnier
2017-07-07 16:31   ` Fabrice Gasnier
2017-07-07 16:31   ` Fabrice Gasnier
2017-07-10 15:27   ` Rob Herring
2017-07-10 15:27     ` Rob Herring
2017-07-10 15:27     ` Rob Herring
2017-07-07 16:31 ` [PATCH v3 6/9] iio: trigger: Add STM32 LPTimer trigger driver Fabrice Gasnier
2017-07-07 16:31   ` Fabrice Gasnier
2017-07-07 16:31   ` Fabrice Gasnier
2017-07-09 20:04   ` Jonathan Cameron
2017-07-09 20:04     ` Jonathan Cameron
2017-07-09 20:04     ` Jonathan Cameron
2017-07-07 16:31 ` [PATCH v3 7/9] dt-bindings: iio: Add STM32 LPTimer quadrature encoder and counter Fabrice Gasnier
2017-07-07 16:31   ` Fabrice Gasnier
2017-07-07 16:31   ` Fabrice Gasnier
2017-07-07 16:31 ` [PATCH v3 8/9] iio: counter: Add support for STM32 LPTimer Fabrice Gasnier
2017-07-07 16:31   ` Fabrice Gasnier
2017-07-07 16:31   ` Fabrice Gasnier
2017-07-09 20:12   ` Jonathan Cameron
2017-07-09 20:12     ` Jonathan Cameron
2017-07-09 20:12     ` Jonathan Cameron
2017-07-09 20:12     ` Jonathan Cameron
2017-07-10 12:54     ` William Breathitt Gray
2017-07-10 12:54       ` William Breathitt Gray
2017-07-10 12:54       ` William Breathitt Gray
2017-07-10 12:54       ` William Breathitt Gray
2017-07-07 16:31 ` [PATCH v3 9/9] iio: adc: stm32: add support for lptimer triggers Fabrice Gasnier
2017-07-07 16:31   ` Fabrice Gasnier
2017-07-07 16:31   ` Fabrice Gasnier
2017-07-09 20:13   ` Jonathan Cameron
2017-07-09 20:13     ` Jonathan Cameron
2017-07-09 20:13     ` Jonathan Cameron
2017-08-21  7:24 ` [PATCH v3 0/9] Add STM32 LPTimer: PWM, trigger and counter Lee Jones
2017-08-21  7:24   ` Lee Jones
2017-08-21  7:24   ` Lee Jones

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.