The following patches enable pwm and IIO Timer features for stm32 platforms.
Those two features are mixed into the registers of the same hardware block (named timer) which lead to introduce a multifunctions driver on the top to be able to share the registers.
In stm32 14 instances of timer hardware block exist, even if they all have the same register mapping they could have a different number of pwm channels and/or different triggers capabilities. To keep the code as simple as possible we use compatible and platform_data to distinguish them.
The MFD (stm32-mfd-timer.c) takes care of clock, interrupt and register mapping by using regmap. stm32_mfd_timer_dev structure is provided to its children to share those information.
PWM driver is implemented into pwm-stm32.c. Depending of the instance we may have up to 4 channels, sometime with complementary outputs or 32 bits counter instead of 16 bits. Some hardware blocks may also have a break input function which allows to stop pwm depending of a level, defined in devicetree, on an external pin.
IIO timer driver (stm32-iio-timer.c and stm32-iio-timers.h) define a list of hardware triggers usable by hardware blocks like ADC, DAC or other timers.
The matrix of possible connections between blocks is quite complex so we use trigger names and is_stm32_iio_timer_trigger() function to be sure that triggers are valid and configure the IPs.
Timer hardware blocks can configure (through "master_mode" IIO device attribute) which internal signal (counter enable, reset, comparison block, etc...) is used to generate the trigger.
By using "slave_mode" IIO device attribute timer can also configure on which event (level, rising edge) of the block is enabled.
Since we can use trigger from one hardware to control an other block, we can use a pwm to control an other one. The following example shows how to configure pwm1 and pwm3 to make pwm3 generate pulse only when pwm1 pulse level is high.
/sys/bus/iio/devices # ls iio:device0 iio:device1 trigger0 trigger1
configure timer1 to use pwm1 channel 0 as output trigger /sys/bus/iio/devices # echo 4 > iio:device0/master_mode configure timer3 to enable only when input is high /sys/bus/iio/devices # echo 5 > iio:device1/slave_mode /sys/bus/iio/devices # cat trigger0/name tim1_trgo configure timer2 to use timer1 trigger is input /sys/bus/iio/devices # echo "tim1_trgo" > iio:device1/trigger/current_trigger
configure pwm3 channel 0 to generate a signal with a period of 100ms and a duty cycle of 50% /sys/devices/platform/soc/40000400.mfdtimer3/pwm3/pwm/pwmchip4 # echo 0 > export /sys/devices/platform/soc/40000400.mfdtimer3/pwm3/pwm/pwmchip4 # echo 100000000 > pwm0/period /sys/devices/platform/soc/40000400.mfdtimer3/pwm3/pwm/pwmchip4 # echo 50000000 > pwm0/duty_cycle /sys/devices/platform/soc/40000400.mfdtimer3/pwm3/pwm/pwmchip4 # echo 1 > pwm0/enable here pwm3 channel 0, as expected, doesn't start because has to be triggered by pwm1 channel 0
configure pwm1 channel 0 to generate a signal with a period of 1s and a duty cycle of 50% /sys/devices/platform/soc/40010000.mfdtimer1/pwm1/pwm/pwmchip0 # echo 0 > export /sys/devices/platform/soc/40010000.mfdtimer1/pwm1/pwm/pwmchip0 # echo 1000000000 > pwm0/period /sys/devices/platform/soc/40010000.mfdtimer1/pwm1/pwm/pwmchip0 # echo 500000000 > pwm0/duty_cycle /sys/devices/platform/soc/40010000.mfdtimer1/pwm1/pwm/pwmchip0 # echo 1 > pwm0/enable finally pwm1 starts and pwm3 only generates pulse when pwm1 signal is high
An other example to use a timer as source of clock for another device. Here timer1 is used a source clock for pwm3:
/sys/bus/iio/devices # echo 100000 > trigger0/sampling_frequency /sys/bus/iio/devices # echo tim1_trgo > iio:device1/trigger/current_trigger /sys/bus/iio/devices # echo 7 > iio:device1/slave_mode /sys/devices/platform/soc/40000400.mfdtimer3/pwm3/pwm/pwmchip4 # echo 0 > export /sys/devices/platform/soc/40000400.mfdtimer3/pwm3/pwm/pwmchip4 # echo 1000000 > pwm0/period /sys/devices/platform/soc/40000400.mfdtimer3/pwm3/pwm/pwmchip4 # echo 500000 > pwm0/duty_cycle /sys/devices/platform/soc/40000400.mfdtimer3/pwm3/pwm/pwmchip4 # echo 1 > pwm0/enable
Benjamin Gaignard (7): add binding for stm32 multifunctions timer driver add MFD for stm32 timer IP add pwm-stm32 DT bindings add pwm driver for stm32 plaftorm add bindings for stm32 IIO timer drivers add STM32 IIO timer driver add stm32 multi-functions timer driver in DT
.../bindings/iio/timer/stm32-iio-timer.txt | 33 + .../devicetree/bindings/mfd/stm32-timer.txt | 53 ++ .../devicetree/bindings/pwm/pwm-stm32.txt | 43 ++ arch/arm/boot/dts/stm32f429.dtsi | 246 +++++++ arch/arm/boot/dts/stm32f469-disco.dts | 29 + drivers/iio/Kconfig | 2 +- drivers/iio/Makefile | 1 + drivers/iio/timer/Kconfig | 15 + drivers/iio/timer/Makefile | 1 + drivers/iio/timer/stm32-iio-timer.c | 766 +++++++++++++++++++++ drivers/iio/trigger/Kconfig | 1 - drivers/mfd/Kconfig | 10 + drivers/mfd/Makefile | 2 + drivers/mfd/stm32-mfd-timer.c | 236 +++++++ drivers/pwm/Kconfig | 8 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-stm32.c | 358 ++++++++++ include/linux/iio/timer/stm32-iio-timers.h | 25 + include/linux/mfd/stm32-mfd-timer.h | 78 +++ 19 files changed, 1906 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/iio/timer/stm32-iio-timer.txt create mode 100644 Documentation/devicetree/bindings/mfd/stm32-timer.txt create mode 100644 Documentation/devicetree/bindings/pwm/pwm-stm32.txt create mode 100644 drivers/iio/timer/Kconfig create mode 100644 drivers/iio/timer/Makefile create mode 100644 drivers/iio/timer/stm32-iio-timer.c create mode 100644 drivers/mfd/stm32-mfd-timer.c create mode 100644 drivers/pwm/pwm-stm32.c create mode 100644 include/linux/iio/timer/stm32-iio-timers.h create mode 100644 include/linux/mfd/stm32-mfd-timer.h
Add bindings information for stm32 timer MFD
Signed-off-by: Benjamin Gaignard benjamin.gaignard@st.com --- .../devicetree/bindings/mfd/stm32-timer.txt | 53 ++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/stm32-timer.txt
diff --git a/Documentation/devicetree/bindings/mfd/stm32-timer.txt b/Documentation/devicetree/bindings/mfd/stm32-timer.txt new file mode 100644 index 0000000..3cefce1 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/stm32-timer.txt @@ -0,0 +1,53 @@ +STM32 multifunctions timer driver + +stm32 timer MFD allow to handle at the same time pwm and IIO timer devices + +Required parameters: +- compatible: must be one of the follow value: + "st,stm32-mfd-timer1" + "st,stm32-mfd-timer2" + "st,stm32-mfd-timer3" + "st,stm32-mfd-timer4" + "st,stm32-mfd-timer5" + "st,stm32-mfd-timer6" + "st,stm32-mfd-timer7" + "st,stm32-mfd-timer8" + "st,stm32-mfd-timer9" + "st,stm32-mfd-timer10" + "st,stm32-mfd-timer11" + "st,stm32-mfd-timer12" + "st,stm32-mfd-timer13" + "st,stm32-mfd-timer14" + +- reg : Physical base address and length of the controller's + registers. +- clock-names: Set to "mfd_timer_clk". +- clocks: Phandle of the clock used by the timer module. + For Clk properties, please refer to [1]. +- interrupts : Reference to the timer interrupt + +Optional parameters: +- resets : Reference to a reset controller asserting the timer + +Optional subnodes: +- pwm: See Documentation/devicetree/bindings/pwm/pwm-stm32.txt +- iiotimer: See Documentation/devicetree/bindings/iio/timer/stm32-iio-timer.txt + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt + +Example: + mfd_timer1: mfdtimer1@40010000 { + compatible = "st,stm32-mfd-timer1"; + reg = <0x40010000 0x400>; + clocks = <&rcc 0 160>; + clock-names = "mfd_timer_clk"; + interrupts = <27>; + + pwm1: pwm1@40010000 { + compatible = "st,stm32-pwm1"; + }; + + iiotimer1: iiotimer1@40010000 { + compatible = "st,stm32-iio-timer1"; + }; + };
On Tue, 22 Nov 2016, Benjamin Gaignard wrote:
Add bindings information for stm32 timer MFD
Signed-off-by: Benjamin Gaignard benjamin.gaignard@st.com
.../devicetree/bindings/mfd/stm32-timer.txt | 53 ++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/stm32-timer.txt
diff --git a/Documentation/devicetree/bindings/mfd/stm32-timer.txt b/Documentation/devicetree/bindings/mfd/stm32-timer.txt new file mode 100644 index 0000000..3cefce1 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/stm32-timer.txt @@ -0,0 +1,53 @@ +STM32 multifunctions timer driver
"STM32 Multi-Function Timer/PWM device bindings"
Doesn't this shared device have a better name?
+stm32 timer MFD allow to handle at the same time pwm and IIO timer devices
No need for this sentence.
+Required parameters: +- compatible: must be one of the follow value:
- "st,stm32-mfd-timer1"
- "st,stm32-mfd-timer2"
- "st,stm32-mfd-timer3"
- "st,stm32-mfd-timer4"
- "st,stm32-mfd-timer5"
- "st,stm32-mfd-timer6"
- "st,stm32-mfd-timer7"
- "st,stm32-mfd-timer8"
- "st,stm32-mfd-timer9"
- "st,stm32-mfd-timer10"
- "st,stm32-mfd-timer11"
- "st,stm32-mfd-timer12"
- "st,stm32-mfd-timer13"
- "st,stm32-mfd-timer14"
We don't normally number devices.
What's stopping you from simply doing:
pwm1: pwm1@40010000 { compatible = "st,stm32-pwm"; }; pwm2: pwm1@40020000 { compatible = "st,stm32-pwm"; }; pwm3: pwm1@40030000 { compatible = "st,stm32-pwm"; };
+- reg : Physical base address and length of the controller's
registers.
+- clock-names: Set to "mfd_timer_clk".
How many clocks are there?
If only 1, you don't need this property.
"mfd_timer_clk" is not the correct name.
What is it called in the datasheet?
+- clocks: Phandle of the clock used by the timer module.
"Phandle to the clock ..."
For Clk properties, please refer to [1].
+- interrupts : Reference to the timer interrupt
Reference to?
See how other binding documents describe this property.
+Optional parameters: +- resets : Reference to a reset controller asserting the timer
As above.
+Optional subnodes:
Either use ":" or " :" or "<tab>:", but keep it consistent.
+- pwm: See Documentation/devicetree/bindings/pwm/pwm-stm32.txt +- iiotimer: See Documentation/devicetree/bindings/iio/timer/stm32-iio-timer.txt
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
Use the relative paths "../clock/", "../pwm/", "../iio/".
+Example:
- mfd_timer1: mfdtimer1@40010000 {
This is not an "MFD timer". MFD is a Linuxisum.
compatible = "st,stm32-mfd-timer1";
Better description required.
reg = <0x40010000 0x400>;
clocks = <&rcc 0 160>;
clock-names = "mfd_timer_clk";
interrupts = <27>;
pwm1: pwm1@40010000 {
compatible = "st,stm32-pwm1";
};
iiotimer1: iiotimer1@40010000 {
compatible = "st,stm32-iio-timer1";
};
- };
2016-11-22 17:52 GMT+01:00 Lee Jones lee.jones@linaro.org:
On Tue, 22 Nov 2016, Benjamin Gaignard wrote:
Add bindings information for stm32 timer MFD
Signed-off-by: Benjamin Gaignard benjamin.gaignard@st.com
.../devicetree/bindings/mfd/stm32-timer.txt | 53 ++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/stm32-timer.txt
diff --git a/Documentation/devicetree/bindings/mfd/stm32-timer.txt b/Documentation/devicetree/bindings/mfd/stm32-timer.txt new file mode 100644 index 0000000..3cefce1 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/stm32-timer.txt @@ -0,0 +1,53 @@ +STM32 multifunctions timer driver
"STM32 Multi-Function Timer/PWM device bindings"
Doesn't this shared device have a better name?
In SoC documentation those hardware blocks are named "advanced-control timers", "general purpose timers" or "basic timers" "stm32-timer" name is already used for clock source driver, that why I have prefix it with mfd
+stm32 timer MFD allow to handle at the same time pwm and IIO timer devices
No need for this sentence.
OK
+Required parameters: +- compatible: must be one of the follow value:
"st,stm32-mfd-timer1"
"st,stm32-mfd-timer2"
"st,stm32-mfd-timer3"
"st,stm32-mfd-timer4"
"st,stm32-mfd-timer5"
"st,stm32-mfd-timer6"
"st,stm32-mfd-timer7"
"st,stm32-mfd-timer8"
"st,stm32-mfd-timer9"
"st,stm32-mfd-timer10"
"st,stm32-mfd-timer11"
"st,stm32-mfd-timer12"
"st,stm32-mfd-timer13"
"st,stm32-mfd-timer14"
We don't normally number devices.
What's stopping you from simply doing:
pwm1: pwm1@40010000 { compatible = "st,stm32-pwm"; }; pwm2: pwm1@40020000 { compatible = "st,stm32-pwm"; }; pwm3: pwm1@40030000 { compatible = "st,stm32-pwm"; };
Because each instance of the hardware is slightly different: number of pwm channels, triggers capabilities, etc .. so I need to distinguish them. Since it look to be a problem I will follow your suggestion and add a property this driver to be able to identify each instance. Do you think that "id" parameter (integer for 1 to 14) is acceptable ?
+- reg : Physical base address and length of the controller's
registers.
+- clock-names: Set to "mfd_timer_clk".
Only one but I use devm_regmap_init_mmio_clk() to avoid calling clk_{enable/disable} everywhere in the drivers when reading/writing regsister. devm_regmap_init_mmio_clk() find the clock by it name that why I have put it here In the doc this clock in named "clk_int" I will use this name.
How many clocks are there?
If only 1, you don't need this property.
"mfd_timer_clk" is not the correct name.
What is it called in the datasheet?
+- clocks: Phandle of the clock used by the timer module.
"Phandle to the clock ..."
For Clk properties, please refer to [1].
+- interrupts : Reference to the timer interrupt
Reference to?
See how other binding documents describe this property.
+Optional parameters: +- resets : Reference to a reset controller asserting the timer
As above.
+Optional subnodes:
Either use ":" or " :" or "<tab>:", but keep it consistent.
+- pwm: See Documentation/devicetree/bindings/pwm/pwm-stm32.txt +- iiotimer: See Documentation/devicetree/bindings/iio/timer/stm32-iio-timer.txt
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
Use the relative paths "../clock/", "../pwm/", "../iio/".
OK
+Example:
mfd_timer1: mfdtimer1@40010000 {
This is not an "MFD timer". MFD is a Linuxisum.
compatible = "st,stm32-mfd-timer1";
Better description required.
reg = <0x40010000 0x400>;
clocks = <&rcc 0 160>;
clock-names = "mfd_timer_clk";
interrupts = <27>;
pwm1: pwm1@40010000 {
compatible = "st,stm32-pwm1";
};
iiotimer1: iiotimer1@40010000 {
compatible = "st,stm32-iio-timer1";
};
};
On Wed, 23 Nov 2016, Benjamin Gaignard wrote:
2016-11-22 17:52 GMT+01:00 Lee Jones lee.jones@linaro.org:
On Tue, 22 Nov 2016, Benjamin Gaignard wrote:
Add bindings information for stm32 timer MFD
Signed-off-by: Benjamin Gaignard benjamin.gaignard@st.com
.../devicetree/bindings/mfd/stm32-timer.txt | 53 ++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/stm32-timer.txt
diff --git a/Documentation/devicetree/bindings/mfd/stm32-timer.txt b/Documentation/devicetree/bindings/mfd/stm32-timer.txt new file mode 100644 index 0000000..3cefce1 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/stm32-timer.txt @@ -0,0 +1,53 @@ +STM32 multifunctions timer driver
"STM32 Multi-Function Timer/PWM device bindings"
Doesn't this shared device have a better name?
In SoC documentation those hardware blocks are named "advanced-control timers", "general purpose timers" or "basic timers" "stm32-timer" name is already used for clock source driver, that why I have prefix it with mfd
MFD is a Linuxisum and has no place in hardware description.
Please used one of the names you mentioned above.
Hopefully the one that best fits.
+stm32 timer MFD allow to handle at the same time pwm and IIO timer devices
No need for this sentence.
OK
+Required parameters: +- compatible: must be one of the follow value:
"st,stm32-mfd-timer1"
"st,stm32-mfd-timer2"
"st,stm32-mfd-timer3"
"st,stm32-mfd-timer4"
"st,stm32-mfd-timer5"
"st,stm32-mfd-timer6"
"st,stm32-mfd-timer7"
"st,stm32-mfd-timer8"
"st,stm32-mfd-timer9"
"st,stm32-mfd-timer10"
"st,stm32-mfd-timer11"
"st,stm32-mfd-timer12"
"st,stm32-mfd-timer13"
"st,stm32-mfd-timer14"
We don't normally number devices.
What's stopping you from simply doing:
pwm1: pwm1@40010000 { compatible = "st,stm32-pwm"; }; pwm2: pwm1@40020000 { compatible = "st,stm32-pwm"; }; pwm3: pwm1@40030000 { compatible = "st,stm32-pwm"; };
Because each instance of the hardware is slightly different: number of pwm channels, triggers capabilities, etc .. so I need to distinguish them. Since it look to be a problem I will follow your suggestion and add a property this driver to be able to identify each instance. Do you think that "id" parameter (integer for 1 to 14) is acceptable ?
Unfortunately not. IDs aren't allowed in DT.
What about "pwm-chans" and "trigger"?
pwm-chans : Number of available channels available trigger : Boolean value specifying whether a timer is present
Why can't you let of_platform_populate() register the devices for you? Then you can get rid of all of the meaningless numbers all over the place.
+- reg : Physical base address and length of the controller's
registers.
+- clock-names: Set to "mfd_timer_clk".
Only one but I use devm_regmap_init_mmio_clk() to avoid calling clk_{enable/disable} everywhere in the drivers when reading/writing regsister. devm_regmap_init_mmio_clk() find the clock by it name that why I have put it here In the doc this clock in named "clk_int" I will use this name.
Please reply *below* the quote.
But okay, "clk_int" sounds like a more suitable name.
How many clocks are there?
If only 1, you don't need this property.
"mfd_timer_clk" is not the correct name.
What is it called in the datasheet?
+- clocks: Phandle of the clock used by the timer module.
"Phandle to the clock ..."
For Clk properties, please refer to [1].
+- interrupts : Reference to the timer interrupt
Reference to?
See how other binding documents describe this property.
+Optional parameters: +- resets : Reference to a reset controller asserting the timer
As above.
+Optional subnodes:
Either use ":" or " :" or "<tab>:", but keep it consistent.
+- pwm: See Documentation/devicetree/bindings/pwm/pwm-stm32.txt +- iiotimer: See Documentation/devicetree/bindings/iio/timer/stm32-iio-timer.txt
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
Use the relative paths "../clock/", "../pwm/", "../iio/".
OK
+Example:
mfd_timer1: mfdtimer1@40010000 {
This is not an "MFD timer". MFD is a Linuxisum.
compatible = "st,stm32-mfd-timer1";
Better description required.
reg = <0x40010000 0x400>;
clocks = <&rcc 0 160>;
clock-names = "mfd_timer_clk";
interrupts = <27>;
pwm1: pwm1@40010000 {
compatible = "st,stm32-pwm1";
};
iiotimer1: iiotimer1@40010000 {
compatible = "st,stm32-iio-timer1";
};
};
2016-11-23 10:21 GMT+01:00 Lee Jones lee.jones@linaro.org:
On Wed, 23 Nov 2016, Benjamin Gaignard wrote:
2016-11-22 17:52 GMT+01:00 Lee Jones lee.jones@linaro.org:
On Tue, 22 Nov 2016, Benjamin Gaignard wrote:
Add bindings information for stm32 timer MFD
Signed-off-by: Benjamin Gaignard benjamin.gaignard@st.com
.../devicetree/bindings/mfd/stm32-timer.txt | 53 ++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/stm32-timer.txt
diff --git a/Documentation/devicetree/bindings/mfd/stm32-timer.txt b/Documentation/devicetree/bindings/mfd/stm32-timer.txt new file mode 100644 index 0000000..3cefce1 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/stm32-timer.txt @@ -0,0 +1,53 @@ +STM32 multifunctions timer driver
"STM32 Multi-Function Timer/PWM device bindings"
Doesn't this shared device have a better name?
In SoC documentation those hardware blocks are named "advanced-control timers", "general purpose timers" or "basic timers" "stm32-timer" name is already used for clock source driver, that why I have prefix it with mfd
MFD is a Linuxisum and has no place in hardware description.
Please used one of the names you mentioned above.
I will go for "st,stm32-advanced-timer"
Hopefully the one that best fits.
+stm32 timer MFD allow to handle at the same time pwm and IIO timer devices
No need for this sentence.
OK
+Required parameters: +- compatible: must be one of the follow value:
"st,stm32-mfd-timer1"
"st,stm32-mfd-timer2"
"st,stm32-mfd-timer3"
"st,stm32-mfd-timer4"
"st,stm32-mfd-timer5"
"st,stm32-mfd-timer6"
"st,stm32-mfd-timer7"
"st,stm32-mfd-timer8"
"st,stm32-mfd-timer9"
"st,stm32-mfd-timer10"
"st,stm32-mfd-timer11"
"st,stm32-mfd-timer12"
"st,stm32-mfd-timer13"
"st,stm32-mfd-timer14"
We don't normally number devices.
What's stopping you from simply doing:
pwm1: pwm1@40010000 { compatible = "st,stm32-pwm"; }; pwm2: pwm1@40020000 { compatible = "st,stm32-pwm"; }; pwm3: pwm1@40030000 { compatible = "st,stm32-pwm"; };
Because each instance of the hardware is slightly different: number of pwm channels, triggers capabilities, etc .. so I need to distinguish them. Since it look to be a problem I will follow your suggestion and add a property this driver to be able to identify each instance. Do you think that "id" parameter (integer for 1 to 14) is acceptable ?
Unfortunately not. IDs aren't allowed in DT.
What about "pwm-chans" and "trigger"?
pwm-chans : Number of available channels available
For pwm I need those 4 properties: st,pwm-number: the number of PWM devices st,complementary: if exist have complementary ouput st,32bit-counter: if exist have 32 bits counter st,breakinput-polarity: if set enable break input feature.
Is it acceptable from pwm maintainer point of view ?
trigger : Boolean value specifying whether a timer is present
Following our discussion on IRC I will try to code for your proposal:
advanced-timer@40010000 { compatible = "st,stm32-advanced-timer"; reg = <0x40010000 0x400>; clocks = <&rcc 0 160>; clock-names = "clk_int";
pwm@0 { compatible = "st,stm32-pwm"; st,pwm-number= <4>; st,complementary; st,breakinput; };
timer@0 { reg = <1>; compatible = "st,stm32-iio-timer"; interrupts = <27>; triggers = <5 2 3 4>; }; };
triggers parameter will be used to know which trigger are valid for the IIO device
[snip]
Rob,
Would you mind casting an eye on this please?
On Wed, 23 Nov 2016, Benjamin Gaignard wrote:
2016-11-23 10:21 GMT+01:00 Lee Jones lee.jones@linaro.org:
On Wed, 23 Nov 2016, Benjamin Gaignard wrote:
2016-11-22 17:52 GMT+01:00 Lee Jones lee.jones@linaro.org:
On Tue, 22 Nov 2016, Benjamin Gaignard wrote:
Add bindings information for stm32 timer MFD
Signed-off-by: Benjamin Gaignard benjamin.gaignard@st.com
.../devicetree/bindings/mfd/stm32-timer.txt | 53 ++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/stm32-timer.txt
diff --git a/Documentation/devicetree/bindings/mfd/stm32-timer.txt b/Documentation/devicetree/bindings/mfd/stm32-timer.txt new file mode 100644 index 0000000..3cefce1 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/stm32-timer.txt @@ -0,0 +1,53 @@ +STM32 multifunctions timer driver
"STM32 Multi-Function Timer/PWM device bindings"
Doesn't this shared device have a better name?
In SoC documentation those hardware blocks are named "advanced-control timers", "general purpose timers" or "basic timers" "stm32-timer" name is already used for clock source driver, that why I have prefix it with mfd
MFD is a Linuxisum and has no place in hardware description.
Please used one of the names you mentioned above.
I will go for "st,stm32-advanced-timer"
Hopefully the one that best fits.
+stm32 timer MFD allow to handle at the same time pwm and IIO timer devices
No need for this sentence.
OK
+Required parameters: +- compatible: must be one of the follow value:
"st,stm32-mfd-timer1"
"st,stm32-mfd-timer2"
"st,stm32-mfd-timer3"
"st,stm32-mfd-timer4"
"st,stm32-mfd-timer5"
"st,stm32-mfd-timer6"
"st,stm32-mfd-timer7"
"st,stm32-mfd-timer8"
"st,stm32-mfd-timer9"
"st,stm32-mfd-timer10"
"st,stm32-mfd-timer11"
"st,stm32-mfd-timer12"
"st,stm32-mfd-timer13"
"st,stm32-mfd-timer14"
We don't normally number devices.
What's stopping you from simply doing:
pwm1: pwm1@40010000 { compatible = "st,stm32-pwm"; }; pwm2: pwm1@40020000 { compatible = "st,stm32-pwm"; }; pwm3: pwm1@40030000 { compatible = "st,stm32-pwm"; };
Because each instance of the hardware is slightly different: number of pwm channels, triggers capabilities, etc .. so I need to distinguish them. Since it look to be a problem I will follow your suggestion and add a property this driver to be able to identify each instance. Do you think that "id" parameter (integer for 1 to 14) is acceptable ?
Unfortunately not. IDs aren't allowed in DT.
What about "pwm-chans" and "trigger"?
pwm-chans : Number of available channels available
For pwm I need those 4 properties: st,pwm-number: the number of PWM devices
st,pwm-num-chan is already documented.
Please use that instead of creating new properties.
st,complementary: if exist have complementary ouput st,32bit-counter: if exist have 32 bits counter st,breakinput-polarity: if set enable break input feature.
Is it acceptable from pwm maintainer point of view ?
trigger : Boolean value specifying whether a timer is present
Following our discussion on IRC I will try to code for your proposal:
advanced-timer@40010000 { compatible = "st,stm32-advanced-timer"; reg = <0x40010000 0x400>; clocks = <&rcc 0 160>; clock-names = "clk_int";
pwm@0 { compatible = "st,stm32-pwm"; st,pwm-number= <4>; st,complementary; st,breakinput; }; timer@0 { reg = <1>; compatible = "st,stm32-iio-timer"; interrupts = <27>; triggers = <5 2 3 4>; };
};
triggers parameter will be used to know which trigger are valid for the IIO device
Except for "st,pwm-number" as mentioned above, this looks good to me.
Rob, would what do you think?
This hardware block could at used at same time for PWM generation and IIO timer for other IPs like DAC, ADC or other timers. PWM and IIO timer configuration are mixed in the same registers so we need a MFD to be able to share those registers.
Signed-off-by: Benjamin Gaignard benjamin.gaignard@st.com --- drivers/mfd/Kconfig | 10 ++ drivers/mfd/Makefile | 2 + drivers/mfd/stm32-mfd-timer.c | 236 ++++++++++++++++++++++++++++++++++++ include/linux/mfd/stm32-mfd-timer.h | 78 ++++++++++++ 4 files changed, 326 insertions(+) create mode 100644 drivers/mfd/stm32-mfd-timer.c create mode 100644 include/linux/mfd/stm32-mfd-timer.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index c6df644..63aee36 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1607,6 +1607,15 @@ config MFD_STW481X in various ST Microelectronics and ST-Ericsson embedded Nomadik series.
+config MFD_STM32_TIMER + tristate "Support for STM32 multifunctions timer" + select MFD_CORE + select REGMAP + depends on ARCH_STM32 + depends on OF + help + Select multifunction driver (pwm, IIO trigger) for stm32 timers + menu "Multimedia Capabilities Port drivers" depends on ARCH_SA1100
@@ -1644,4 +1653,5 @@ config MFD_VEXPRESS_SYSREG on the ARM Ltd. Versatile Express board.
endmenu + endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 9834e66..b348c3e 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -211,3 +211,5 @@ obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o obj-$(CONFIG_MFD_MT6397) += mt6397-core.o
obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o + +obj-$(CONFIG_MFD_STM32_TIMER) += stm32-mfd-timer.o diff --git a/drivers/mfd/stm32-mfd-timer.c b/drivers/mfd/stm32-mfd-timer.c new file mode 100644 index 0000000..67e7db3 --- /dev/null +++ b/drivers/mfd/stm32-mfd-timer.c @@ -0,0 +1,236 @@ +/* + * stm32-timer.c + * + * Copyright (C) STMicroelectronics 2016 + * Author: Benjamin Gaignard benjamin.gaignard@st.com for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/of.h> + +#include <linux/mfd/stm32-mfd-timer.h> + +static const struct stm32_mfd_timer_cfg mfd_cells_cfg[] = { + { + .pwm_name = "pwm1", + .pwm_compatible = "st,stm32-pwm1", + .trigger_name = "iiotimer1", + .trigger_compatible = "st,stm32-iio-timer1", + }, + { + .pwm_name = "pwm2", + .pwm_compatible = "st,stm32-pwm2", + .trigger_name = "iiotimer2", + .trigger_compatible = "st,stm32-iio-timer2", + }, + { + .pwm_name = "pwm3", + .pwm_compatible = "st,stm32-pwm3", + .trigger_name = "iiotimer3", + .trigger_compatible = "st,stm32-iio-timer3", + }, + { + .pwm_name = "pwm4", + .pwm_compatible = "st,stm32-pwm4", + .trigger_name = "iiotimer4", + .trigger_compatible = "st,stm32-iio-timer4", + }, + { + .pwm_name = "pwm5", + .pwm_compatible = "st,stm32-pwm5", + .trigger_name = "iiotimer5", + .trigger_compatible = "st,stm32-iio-timer5", + }, + { + .trigger_name = "iiotimer6", + .trigger_compatible = "st,stm32-iio-timer6", + }, + { + .trigger_name = "iiotimer7", + .trigger_compatible = "st,stm32-iio-timer7", + }, + { + .pwm_name = "pwm8", + .pwm_compatible = "st,stm32-pwm8", + .trigger_name = "iiotimer8", + .trigger_compatible = "st,stm32-iio-timer8", + }, + { + .pwm_name = "pwm9", + .pwm_compatible = "st,stm32-pwm9", + .trigger_name = "iiotimer9", + .trigger_compatible = "st,stm32-iio-timer9", + }, + { + .pwm_name = "pwm10", + .pwm_compatible = "st,stm32-pwm10", + }, + { + .pwm_name = "pwm11", + .pwm_compatible = "st,stm32-pwm11", + }, + { + .pwm_name = "pwm12", + .pwm_compatible = "st,stm32-pwm12", + .trigger_name = "iiotimer12", + .trigger_compatible = "st,stm32-iio-timer12", + }, + { + .pwm_name = "pwm13", + .pwm_compatible = "st,stm32-pwm13", + }, + { + .pwm_name = "pwm14", + .pwm_compatible = "st,stm32-pwm14", + }, +}; + +static const struct of_device_id stm32_timer_of_match[] = { + { + .compatible = "st,stm32-mfd-timer1", + .data = &mfd_cells_cfg[0], + }, + { + .compatible = "st,stm32-mfd-timer2", + .data = &mfd_cells_cfg[1], + }, + { + .compatible = "st,stm32-mfd-timer3", + .data = &mfd_cells_cfg[2], + }, + { + .compatible = "st,stm32-mfd-timer4", + .data = &mfd_cells_cfg[3], + }, + { + .compatible = "st,stm32-mfd-timer5", + .data = &mfd_cells_cfg[4], + }, + { + .compatible = "st,stm32-mfd-timer6", + .data = &mfd_cells_cfg[5], + }, + { + .compatible = "st,stm32-mfd-timer7", + .data = &mfd_cells_cfg[6], + }, + { + .compatible = "st,stm32-mfd-timer8", + .data = &mfd_cells_cfg[7], + }, + { + .compatible = "st,stm32-mfd-timer9", + .data = &mfd_cells_cfg[8], + }, + { + .compatible = "st,stm32-mfd-timer10", + .data = &mfd_cells_cfg[9], + }, + { + .compatible = "st,stm32-mfd-timer11", + .data = &mfd_cells_cfg[10], + }, + { + .compatible = "st,stm32-mfd-timer12", + .data = &mfd_cells_cfg[11], + }, + { + .compatible = "st,stm32-mfd-timer13", + .data = &mfd_cells_cfg[12], + }, + { + .compatible = "st,stm32-mfd-timer14", + .data = &mfd_cells_cfg[13], + }, +}; + +static const struct regmap_config stm32_timer_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = sizeof(u32), + .max_register = 0x400, + .fast_io = true, +}; + +static int stm32_mfd_timer_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct stm32_mfd_timer_dev *mfd; + struct resource *res; + int ret, nb_cells = 0; + struct mfd_cell *cell = NULL; + void __iomem *mmio; + + mfd = devm_kzalloc(dev, sizeof(*mfd), GFP_KERNEL); + if (!mfd) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOMEM; + + mmio = devm_ioremap_resource(dev, res); + if (IS_ERR(mmio)) + return PTR_ERR(mmio); + + mfd->regmap = devm_regmap_init_mmio_clk(dev, "mfd_timer_clk", mmio, + &stm32_timer_regmap_cfg); + if (IS_ERR(mfd->regmap)) + return PTR_ERR(mfd->regmap); + + mfd->clk = devm_clk_get(dev, NULL); + if (IS_ERR(mfd->clk)) + return PTR_ERR(mfd->clk); + + mfd->irq = platform_get_irq(pdev, 0); + if (mfd->irq < 0) + return -EINVAL; + + /* populate data structure depending on compatibility */ + if (!of_match_node(stm32_timer_of_match, np)->data) + return -EINVAL; + + mfd->cfg = + (struct stm32_mfd_timer_cfg *)of_match_node(stm32_timer_of_match, np)->data; + + if (mfd->cfg->pwm_name && mfd->cfg->pwm_compatible) { + cell = &mfd->cells[nb_cells++]; + cell->name = mfd->cfg->pwm_name; + cell->of_compatible = mfd->cfg->pwm_compatible; + cell->platform_data = mfd; + cell->pdata_size = sizeof(*mfd); + } + + if (mfd->cfg->trigger_name && mfd->cfg->trigger_compatible) { + cell = &mfd->cells[nb_cells++]; + cell->name = mfd->cfg->trigger_name; + cell->of_compatible = mfd->cfg->trigger_compatible; + cell->platform_data = mfd; + cell->pdata_size = sizeof(*mfd); + } + + ret = devm_mfd_add_devices(&pdev->dev, pdev->id, mfd->cells, + nb_cells, NULL, 0, NULL); + if (ret) + return ret; + + platform_set_drvdata(pdev, mfd); + + return 0; +} + +static struct platform_driver stm32_mfd_timer_driver = { + .probe = stm32_mfd_timer_probe, + .driver = { + .name = "stm32-mfd-timer", + .of_match_table = stm32_timer_of_match, + }, +}; +module_platform_driver(stm32_mfd_timer_driver); + +MODULE_DESCRIPTION("STMicroelectronics STM32 Timer MFD"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/stm32-mfd-timer.h b/include/linux/mfd/stm32-mfd-timer.h new file mode 100644 index 0000000..4a79c22 --- /dev/null +++ b/include/linux/mfd/stm32-mfd-timer.h @@ -0,0 +1,78 @@ +/* + * stm32-mfd-timer.h + * + * Copyright (C) STMicroelectronics 2016 + * Author: Benjamin Gaignard benjamin.gaignard@st.com for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _LINUX_MFD_STM32_TIMER_H_ +#define _LINUX_MFD_STM32_TIMER_H_ + +#include <linux/clk.h> +#include <linux/mfd/core.h> +#include <linux/regmap.h> +#include <linux/reset.h> + +#define TIM_CR1 0x00 /* Control Register 1 */ +#define TIM_CR2 0x04 /* Control Register 2 */ +#define TIM_SMCR 0x08 /* Slave mode control reg */ +#define TIM_DIER 0x0C /* DMA/interrupt register */ +#define TIM_SR 0x10 /* Status register */ +#define TIM_EGR 0x14 /* Event Generation Reg */ +#define TIM_CCMR1 0x18 /* Capt/Comp 1 Mode Reg */ +#define TIM_CCMR2 0x1C /* Capt/Comp 2 Mode Reg */ +#define TIM_CCER 0x20 /* Capt/Comp Enable Reg */ +#define TIM_PSC 0x28 /* Prescaler */ +#define TIM_ARR 0x2c /* Auto-Reload Register */ +#define TIM_CCR1 0x34 /* Capt/Comp Register 1 */ +#define TIM_CCR2 0x38 /* Capt/Comp Register 2 */ +#define TIM_CCR3 0x3C /* Capt/Comp Register 3 */ +#define TIM_CCR4 0x40 /* Capt/Comp Register 4 */ +#define TIM_BDTR 0x44 /* Break and Dead-Time Reg */ + +#define TIM_CR1_CEN BIT(0) /* Counter Enable */ +#define TIM_CR1_ARPE BIT(7) /* Auto-reload Preload Ena */ +#define TIM_CR2_MMS (BIT(4) | BIT(5) | BIT(6)) /* Master mode selection */ +#define TIM_SMCR_SMS (BIT(0) | BIT(1) | BIT(2)) /* Slave mode selection */ +#define TIM_SMCR_TS (BIT(4) | BIT(5) | BIT(6)) /* Trigger selection */ +#define TIM_DIER_UIE BIT(0) /* Update interrupt */ +#define TIM_SR_UIF BIT(0) /* Update interrupt flag */ +#define TIM_EGR_UG BIT(0) /* Update Generation */ +#define TIM_CCMR_PE BIT(3) /* Channel Preload Enable */ +#define TIM_CCMR_M1 (BIT(6) | BIT(5)) /* Channel PWM Mode 1 */ +#define TIM_CCER_CC1E BIT(0) /* Capt/Comp 1 out Ena */ +#define TIM_CCER_CC1P BIT(1) /* Capt/Comp 1 Polarity */ +#define TIM_CCER_CC1NE BIT(2) /* Capt/Comp 1N out Ena */ +#define TIM_CCER_CC1NP BIT(3) /* Capt/Comp 1N Polarity */ +#define TIM_CCER_CCXE (BIT(0) | BIT(4) | BIT(8) | BIT(12)) +#define TIM_BDTR_BKE BIT(12) /* Break input enable */ +#define TIM_BDTR_BKP BIT(13) /* Break input polarity */ +#define TIM_BDTR_AOE BIT(14) /* Automatic Output Enable */ +#define TIM_BDTR_MOE BIT(15) /* Main Output Enable */ + +#define STM32_TIMER_CELLS 2 +#define MAX_TIM_PSC 0xFFFF + +struct stm32_mfd_timer_cfg { + const char *pwm_name; + const char *pwm_compatible; + const char *trigger_name; + const char *trigger_compatible; +}; + +struct stm32_mfd_timer_dev { + /* Device data */ + struct device *dev; + struct clk *clk; + int irq; + + /* Registers mapping */ + struct regmap *regmap; + + /* Private data */ + struct mfd_cell cells[STM32_TIMER_CELLS]; + struct stm32_mfd_timer_cfg *cfg; +}; + +#endif
On Tue, 22 Nov 2016, Benjamin Gaignard wrote:
This hardware block could at used at same time for PWM generation and IIO timer for other IPs like DAC, ADC or other timers. PWM and IIO timer configuration are mixed in the same registers so we need a MFD to be able to share those registers.
Signed-off-by: Benjamin Gaignard benjamin.gaignard@st.com
drivers/mfd/Kconfig | 10 ++ drivers/mfd/Makefile | 2 + drivers/mfd/stm32-mfd-timer.c | 236 ++++++++++++++++++++++++++++++++++++ include/linux/mfd/stm32-mfd-timer.h | 78 ++++++++++++ 4 files changed, 326 insertions(+) create mode 100644 drivers/mfd/stm32-mfd-timer.c create mode 100644 include/linux/mfd/stm32-mfd-timer.h
This driver is going to need a re-write.
However, it's difficult to provide suggestions, since I've been left off of the Cc: list for all the other patches.
Please re-send the set with all of the Maintainers Cc'ed on all of the patches.
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index c6df644..63aee36 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1607,6 +1607,15 @@ config MFD_STW481X in various ST Microelectronics and ST-Ericsson embedded Nomadik series. +config MFD_STM32_TIMER
- tristate "Support for STM32 multifunctions timer"
- select MFD_CORE
- select REGMAP
- depends on ARCH_STM32
- depends on OF
- help
Select multifunction driver (pwm, IIO trigger) for stm32 timers
menu "Multimedia Capabilities Port drivers" depends on ARCH_SA1100 @@ -1644,4 +1653,5 @@ config MFD_VEXPRESS_SYSREG on the ARM Ltd. Versatile Express board. endmenu
endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 9834e66..b348c3e 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -211,3 +211,5 @@ obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o obj-$(CONFIG_MFD_MT6397) += mt6397-core.o obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o
+obj-$(CONFIG_MFD_STM32_TIMER) += stm32-mfd-timer.o diff --git a/drivers/mfd/stm32-mfd-timer.c b/drivers/mfd/stm32-mfd-timer.c new file mode 100644 index 0000000..67e7db3 --- /dev/null +++ b/drivers/mfd/stm32-mfd-timer.c @@ -0,0 +1,236 @@ +/*
- stm32-timer.c
- Copyright (C) STMicroelectronics 2016
- Author: Benjamin Gaignard benjamin.gaignard@st.com for STMicroelectronics.
- License terms: GNU General Public License (GPL), version 2
- */
+#include <linux/device.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/of.h>
+#include <linux/mfd/stm32-mfd-timer.h>
+static const struct stm32_mfd_timer_cfg mfd_cells_cfg[] = {
- {
.pwm_name = "pwm1",
.pwm_compatible = "st,stm32-pwm1",
.trigger_name = "iiotimer1",
.trigger_compatible = "st,stm32-iio-timer1",
- },
- {
.pwm_name = "pwm2",
.pwm_compatible = "st,stm32-pwm2",
.trigger_name = "iiotimer2",
.trigger_compatible = "st,stm32-iio-timer2",
- },
- {
.pwm_name = "pwm3",
.pwm_compatible = "st,stm32-pwm3",
.trigger_name = "iiotimer3",
.trigger_compatible = "st,stm32-iio-timer3",
- },
- {
.pwm_name = "pwm4",
.pwm_compatible = "st,stm32-pwm4",
.trigger_name = "iiotimer4",
.trigger_compatible = "st,stm32-iio-timer4",
- },
- {
.pwm_name = "pwm5",
.pwm_compatible = "st,stm32-pwm5",
.trigger_name = "iiotimer5",
.trigger_compatible = "st,stm32-iio-timer5",
- },
- {
.trigger_name = "iiotimer6",
.trigger_compatible = "st,stm32-iio-timer6",
- },
- {
.trigger_name = "iiotimer7",
.trigger_compatible = "st,stm32-iio-timer7",
- },
- {
.pwm_name = "pwm8",
.pwm_compatible = "st,stm32-pwm8",
.trigger_name = "iiotimer8",
.trigger_compatible = "st,stm32-iio-timer8",
- },
- {
.pwm_name = "pwm9",
.pwm_compatible = "st,stm32-pwm9",
.trigger_name = "iiotimer9",
.trigger_compatible = "st,stm32-iio-timer9",
- },
- {
.pwm_name = "pwm10",
.pwm_compatible = "st,stm32-pwm10",
- },
- {
.pwm_name = "pwm11",
.pwm_compatible = "st,stm32-pwm11",
- },
- {
.pwm_name = "pwm12",
.pwm_compatible = "st,stm32-pwm12",
.trigger_name = "iiotimer12",
.trigger_compatible = "st,stm32-iio-timer12",
- },
- {
.pwm_name = "pwm13",
.pwm_compatible = "st,stm32-pwm13",
- },
- {
.pwm_name = "pwm14",
.pwm_compatible = "st,stm32-pwm14",
- },
+};
+static const struct of_device_id stm32_timer_of_match[] = {
- {
.compatible = "st,stm32-mfd-timer1",
.data = &mfd_cells_cfg[0],
- },
- {
.compatible = "st,stm32-mfd-timer2",
.data = &mfd_cells_cfg[1],
- },
- {
.compatible = "st,stm32-mfd-timer3",
.data = &mfd_cells_cfg[2],
- },
- {
.compatible = "st,stm32-mfd-timer4",
.data = &mfd_cells_cfg[3],
- },
- {
.compatible = "st,stm32-mfd-timer5",
.data = &mfd_cells_cfg[4],
- },
- {
.compatible = "st,stm32-mfd-timer6",
.data = &mfd_cells_cfg[5],
- },
- {
.compatible = "st,stm32-mfd-timer7",
.data = &mfd_cells_cfg[6],
- },
- {
.compatible = "st,stm32-mfd-timer8",
.data = &mfd_cells_cfg[7],
- },
- {
.compatible = "st,stm32-mfd-timer9",
.data = &mfd_cells_cfg[8],
- },
- {
.compatible = "st,stm32-mfd-timer10",
.data = &mfd_cells_cfg[9],
- },
- {
.compatible = "st,stm32-mfd-timer11",
.data = &mfd_cells_cfg[10],
- },
- {
.compatible = "st,stm32-mfd-timer12",
.data = &mfd_cells_cfg[11],
- },
- {
.compatible = "st,stm32-mfd-timer13",
.data = &mfd_cells_cfg[12],
- },
- {
.compatible = "st,stm32-mfd-timer14",
.data = &mfd_cells_cfg[13],
- },
+};
+static const struct regmap_config stm32_timer_regmap_cfg = {
- .reg_bits = 32,
- .val_bits = 32,
- .reg_stride = sizeof(u32),
- .max_register = 0x400,
- .fast_io = true,
+};
+static int stm32_mfd_timer_probe(struct platform_device *pdev) +{
- struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
- struct stm32_mfd_timer_dev *mfd;
- struct resource *res;
- int ret, nb_cells = 0;
- struct mfd_cell *cell = NULL;
- void __iomem *mmio;
- mfd = devm_kzalloc(dev, sizeof(*mfd), GFP_KERNEL);
- if (!mfd)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
return -ENOMEM;
- mmio = devm_ioremap_resource(dev, res);
- if (IS_ERR(mmio))
return PTR_ERR(mmio);
- mfd->regmap = devm_regmap_init_mmio_clk(dev, "mfd_timer_clk", mmio,
&stm32_timer_regmap_cfg);
- if (IS_ERR(mfd->regmap))
return PTR_ERR(mfd->regmap);
- mfd->clk = devm_clk_get(dev, NULL);
- if (IS_ERR(mfd->clk))
return PTR_ERR(mfd->clk);
- mfd->irq = platform_get_irq(pdev, 0);
- if (mfd->irq < 0)
return -EINVAL;
- /* populate data structure depending on compatibility */
- if (!of_match_node(stm32_timer_of_match, np)->data)
return -EINVAL;
- mfd->cfg =
- (struct stm32_mfd_timer_cfg *)of_match_node(stm32_timer_of_match, np)->data;
- if (mfd->cfg->pwm_name && mfd->cfg->pwm_compatible) {
cell = &mfd->cells[nb_cells++];
cell->name = mfd->cfg->pwm_name;
cell->of_compatible = mfd->cfg->pwm_compatible;
cell->platform_data = mfd;
cell->pdata_size = sizeof(*mfd);
- }
- if (mfd->cfg->trigger_name && mfd->cfg->trigger_compatible) {
cell = &mfd->cells[nb_cells++];
cell->name = mfd->cfg->trigger_name;
cell->of_compatible = mfd->cfg->trigger_compatible;
cell->platform_data = mfd;
cell->pdata_size = sizeof(*mfd);
- }
- ret = devm_mfd_add_devices(&pdev->dev, pdev->id, mfd->cells,
nb_cells, NULL, 0, NULL);
- if (ret)
return ret;
- platform_set_drvdata(pdev, mfd);
- return 0;
+}
+static struct platform_driver stm32_mfd_timer_driver = {
- .probe = stm32_mfd_timer_probe,
- .driver = {
.name = "stm32-mfd-timer",
.of_match_table = stm32_timer_of_match,
- },
+}; +module_platform_driver(stm32_mfd_timer_driver);
+MODULE_DESCRIPTION("STMicroelectronics STM32 Timer MFD"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/stm32-mfd-timer.h b/include/linux/mfd/stm32-mfd-timer.h new file mode 100644 index 0000000..4a79c22 --- /dev/null +++ b/include/linux/mfd/stm32-mfd-timer.h @@ -0,0 +1,78 @@ +/*
- stm32-mfd-timer.h
- Copyright (C) STMicroelectronics 2016
- Author: Benjamin Gaignard benjamin.gaignard@st.com for STMicroelectronics.
- License terms: GNU General Public License (GPL), version 2
- */
+#ifndef _LINUX_MFD_STM32_TIMER_H_ +#define _LINUX_MFD_STM32_TIMER_H_
+#include <linux/clk.h> +#include <linux/mfd/core.h> +#include <linux/regmap.h> +#include <linux/reset.h>
+#define TIM_CR1 0x00 /* Control Register 1 */ +#define TIM_CR2 0x04 /* Control Register 2 */ +#define TIM_SMCR 0x08 /* Slave mode control reg */ +#define TIM_DIER 0x0C /* DMA/interrupt register */ +#define TIM_SR 0x10 /* Status register */ +#define TIM_EGR 0x14 /* Event Generation Reg */ +#define TIM_CCMR1 0x18 /* Capt/Comp 1 Mode Reg */ +#define TIM_CCMR2 0x1C /* Capt/Comp 2 Mode Reg */ +#define TIM_CCER 0x20 /* Capt/Comp Enable Reg */ +#define TIM_PSC 0x28 /* Prescaler */ +#define TIM_ARR 0x2c /* Auto-Reload Register */ +#define TIM_CCR1 0x34 /* Capt/Comp Register 1 */ +#define TIM_CCR2 0x38 /* Capt/Comp Register 2 */ +#define TIM_CCR3 0x3C /* Capt/Comp Register 3 */ +#define TIM_CCR4 0x40 /* Capt/Comp Register 4 */ +#define TIM_BDTR 0x44 /* Break and Dead-Time Reg */
+#define TIM_CR1_CEN BIT(0) /* Counter Enable */ +#define TIM_CR1_ARPE BIT(7) /* Auto-reload Preload Ena */ +#define TIM_CR2_MMS (BIT(4) | BIT(5) | BIT(6)) /* Master mode selection */ +#define TIM_SMCR_SMS (BIT(0) | BIT(1) | BIT(2)) /* Slave mode selection */ +#define TIM_SMCR_TS (BIT(4) | BIT(5) | BIT(6)) /* Trigger selection */ +#define TIM_DIER_UIE BIT(0) /* Update interrupt */ +#define TIM_SR_UIF BIT(0) /* Update interrupt flag */ +#define TIM_EGR_UG BIT(0) /* Update Generation */ +#define TIM_CCMR_PE BIT(3) /* Channel Preload Enable */ +#define TIM_CCMR_M1 (BIT(6) | BIT(5)) /* Channel PWM Mode 1 */ +#define TIM_CCER_CC1E BIT(0) /* Capt/Comp 1 out Ena */ +#define TIM_CCER_CC1P BIT(1) /* Capt/Comp 1 Polarity */ +#define TIM_CCER_CC1NE BIT(2) /* Capt/Comp 1N out Ena */ +#define TIM_CCER_CC1NP BIT(3) /* Capt/Comp 1N Polarity */ +#define TIM_CCER_CCXE (BIT(0) | BIT(4) | BIT(8) | BIT(12)) +#define TIM_BDTR_BKE BIT(12) /* Break input enable */ +#define TIM_BDTR_BKP BIT(13) /* Break input polarity */ +#define TIM_BDTR_AOE BIT(14) /* Automatic Output Enable */ +#define TIM_BDTR_MOE BIT(15) /* Main Output Enable */
+#define STM32_TIMER_CELLS 2 +#define MAX_TIM_PSC 0xFFFF
+struct stm32_mfd_timer_cfg {
- const char *pwm_name;
- const char *pwm_compatible;
- const char *trigger_name;
- const char *trigger_compatible;
+};
+struct stm32_mfd_timer_dev {
- /* Device data */
- struct device *dev;
- struct clk *clk;
- int irq;
- /* Registers mapping */
- struct regmap *regmap;
- /* Private data */
- struct mfd_cell cells[STM32_TIMER_CELLS];
- struct stm32_mfd_timer_cfg *cfg;
+};
+#endif
On Tue, 22 Nov 2016, Lee Jones wrote:
On Tue, 22 Nov 2016, Benjamin Gaignard wrote:
This hardware block could at used at same time for PWM generation and IIO timer for other IPs like DAC, ADC or other timers. PWM and IIO timer configuration are mixed in the same registers so we need a MFD to be able to share those registers.
Signed-off-by: Benjamin Gaignard benjamin.gaignard@st.com
drivers/mfd/Kconfig | 10 ++ drivers/mfd/Makefile | 2 + drivers/mfd/stm32-mfd-timer.c | 236 ++++++++++++++++++++++++++++++++++++ include/linux/mfd/stm32-mfd-timer.h | 78 ++++++++++++ 4 files changed, 326 insertions(+) create mode 100644 drivers/mfd/stm32-mfd-timer.c create mode 100644 include/linux/mfd/stm32-mfd-timer.h
This driver is going to need a re-write.
However, it's difficult to provide suggestions, since I've been left off of the Cc: list for all the other patches.
Please re-send the set with all of the Maintainers Cc'ed on all of the patches.
Scrap that -- they all just came trickling through!
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index c6df644..63aee36 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1607,6 +1607,15 @@ config MFD_STW481X in various ST Microelectronics and ST-Ericsson embedded Nomadik series. +config MFD_STM32_TIMER
- tristate "Support for STM32 multifunctions timer"
- select MFD_CORE
- select REGMAP
- depends on ARCH_STM32
- depends on OF
- help
Select multifunction driver (pwm, IIO trigger) for stm32 timers
menu "Multimedia Capabilities Port drivers" depends on ARCH_SA1100 @@ -1644,4 +1653,5 @@ config MFD_VEXPRESS_SYSREG on the ARM Ltd. Versatile Express board. endmenu
endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 9834e66..b348c3e 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -211,3 +211,5 @@ obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o obj-$(CONFIG_MFD_MT6397) += mt6397-core.o obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o
+obj-$(CONFIG_MFD_STM32_TIMER) += stm32-mfd-timer.o diff --git a/drivers/mfd/stm32-mfd-timer.c b/drivers/mfd/stm32-mfd-timer.c new file mode 100644 index 0000000..67e7db3 --- /dev/null +++ b/drivers/mfd/stm32-mfd-timer.c @@ -0,0 +1,236 @@ +/*
- stm32-timer.c
- Copyright (C) STMicroelectronics 2016
- Author: Benjamin Gaignard benjamin.gaignard@st.com for STMicroelectronics.
- License terms: GNU General Public License (GPL), version 2
- */
+#include <linux/device.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/of.h>
+#include <linux/mfd/stm32-mfd-timer.h>
+static const struct stm32_mfd_timer_cfg mfd_cells_cfg[] = {
- {
.pwm_name = "pwm1",
.pwm_compatible = "st,stm32-pwm1",
.trigger_name = "iiotimer1",
.trigger_compatible = "st,stm32-iio-timer1",
- },
- {
.pwm_name = "pwm2",
.pwm_compatible = "st,stm32-pwm2",
.trigger_name = "iiotimer2",
.trigger_compatible = "st,stm32-iio-timer2",
- },
- {
.pwm_name = "pwm3",
.pwm_compatible = "st,stm32-pwm3",
.trigger_name = "iiotimer3",
.trigger_compatible = "st,stm32-iio-timer3",
- },
- {
.pwm_name = "pwm4",
.pwm_compatible = "st,stm32-pwm4",
.trigger_name = "iiotimer4",
.trigger_compatible = "st,stm32-iio-timer4",
- },
- {
.pwm_name = "pwm5",
.pwm_compatible = "st,stm32-pwm5",
.trigger_name = "iiotimer5",
.trigger_compatible = "st,stm32-iio-timer5",
- },
- {
.trigger_name = "iiotimer6",
.trigger_compatible = "st,stm32-iio-timer6",
- },
- {
.trigger_name = "iiotimer7",
.trigger_compatible = "st,stm32-iio-timer7",
- },
- {
.pwm_name = "pwm8",
.pwm_compatible = "st,stm32-pwm8",
.trigger_name = "iiotimer8",
.trigger_compatible = "st,stm32-iio-timer8",
- },
- {
.pwm_name = "pwm9",
.pwm_compatible = "st,stm32-pwm9",
.trigger_name = "iiotimer9",
.trigger_compatible = "st,stm32-iio-timer9",
- },
- {
.pwm_name = "pwm10",
.pwm_compatible = "st,stm32-pwm10",
- },
- {
.pwm_name = "pwm11",
.pwm_compatible = "st,stm32-pwm11",
- },
- {
.pwm_name = "pwm12",
.pwm_compatible = "st,stm32-pwm12",
.trigger_name = "iiotimer12",
.trigger_compatible = "st,stm32-iio-timer12",
- },
- {
.pwm_name = "pwm13",
.pwm_compatible = "st,stm32-pwm13",
- },
- {
.pwm_name = "pwm14",
.pwm_compatible = "st,stm32-pwm14",
- },
+};
+static const struct of_device_id stm32_timer_of_match[] = {
- {
.compatible = "st,stm32-mfd-timer1",
.data = &mfd_cells_cfg[0],
- },
- {
.compatible = "st,stm32-mfd-timer2",
.data = &mfd_cells_cfg[1],
- },
- {
.compatible = "st,stm32-mfd-timer3",
.data = &mfd_cells_cfg[2],
- },
- {
.compatible = "st,stm32-mfd-timer4",
.data = &mfd_cells_cfg[3],
- },
- {
.compatible = "st,stm32-mfd-timer5",
.data = &mfd_cells_cfg[4],
- },
- {
.compatible = "st,stm32-mfd-timer6",
.data = &mfd_cells_cfg[5],
- },
- {
.compatible = "st,stm32-mfd-timer7",
.data = &mfd_cells_cfg[6],
- },
- {
.compatible = "st,stm32-mfd-timer8",
.data = &mfd_cells_cfg[7],
- },
- {
.compatible = "st,stm32-mfd-timer9",
.data = &mfd_cells_cfg[8],
- },
- {
.compatible = "st,stm32-mfd-timer10",
.data = &mfd_cells_cfg[9],
- },
- {
.compatible = "st,stm32-mfd-timer11",
.data = &mfd_cells_cfg[10],
- },
- {
.compatible = "st,stm32-mfd-timer12",
.data = &mfd_cells_cfg[11],
- },
- {
.compatible = "st,stm32-mfd-timer13",
.data = &mfd_cells_cfg[12],
- },
- {
.compatible = "st,stm32-mfd-timer14",
.data = &mfd_cells_cfg[13],
- },
+};
+static const struct regmap_config stm32_timer_regmap_cfg = {
- .reg_bits = 32,
- .val_bits = 32,
- .reg_stride = sizeof(u32),
- .max_register = 0x400,
- .fast_io = true,
+};
+static int stm32_mfd_timer_probe(struct platform_device *pdev) +{
- struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
- struct stm32_mfd_timer_dev *mfd;
- struct resource *res;
- int ret, nb_cells = 0;
- struct mfd_cell *cell = NULL;
- void __iomem *mmio;
- mfd = devm_kzalloc(dev, sizeof(*mfd), GFP_KERNEL);
- if (!mfd)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
return -ENOMEM;
- mmio = devm_ioremap_resource(dev, res);
- if (IS_ERR(mmio))
return PTR_ERR(mmio);
- mfd->regmap = devm_regmap_init_mmio_clk(dev, "mfd_timer_clk", mmio,
&stm32_timer_regmap_cfg);
- if (IS_ERR(mfd->regmap))
return PTR_ERR(mfd->regmap);
- mfd->clk = devm_clk_get(dev, NULL);
- if (IS_ERR(mfd->clk))
return PTR_ERR(mfd->clk);
- mfd->irq = platform_get_irq(pdev, 0);
- if (mfd->irq < 0)
return -EINVAL;
- /* populate data structure depending on compatibility */
- if (!of_match_node(stm32_timer_of_match, np)->data)
return -EINVAL;
- mfd->cfg =
- (struct stm32_mfd_timer_cfg *)of_match_node(stm32_timer_of_match, np)->data;
- if (mfd->cfg->pwm_name && mfd->cfg->pwm_compatible) {
cell = &mfd->cells[nb_cells++];
cell->name = mfd->cfg->pwm_name;
cell->of_compatible = mfd->cfg->pwm_compatible;
cell->platform_data = mfd;
cell->pdata_size = sizeof(*mfd);
- }
- if (mfd->cfg->trigger_name && mfd->cfg->trigger_compatible) {
cell = &mfd->cells[nb_cells++];
cell->name = mfd->cfg->trigger_name;
cell->of_compatible = mfd->cfg->trigger_compatible;
cell->platform_data = mfd;
cell->pdata_size = sizeof(*mfd);
- }
- ret = devm_mfd_add_devices(&pdev->dev, pdev->id, mfd->cells,
nb_cells, NULL, 0, NULL);
- if (ret)
return ret;
- platform_set_drvdata(pdev, mfd);
- return 0;
+}
+static struct platform_driver stm32_mfd_timer_driver = {
- .probe = stm32_mfd_timer_probe,
- .driver = {
.name = "stm32-mfd-timer",
.of_match_table = stm32_timer_of_match,
- },
+}; +module_platform_driver(stm32_mfd_timer_driver);
+MODULE_DESCRIPTION("STMicroelectronics STM32 Timer MFD"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/stm32-mfd-timer.h b/include/linux/mfd/stm32-mfd-timer.h new file mode 100644 index 0000000..4a79c22 --- /dev/null +++ b/include/linux/mfd/stm32-mfd-timer.h @@ -0,0 +1,78 @@ +/*
- stm32-mfd-timer.h
- Copyright (C) STMicroelectronics 2016
- Author: Benjamin Gaignard benjamin.gaignard@st.com for STMicroelectronics.
- License terms: GNU General Public License (GPL), version 2
- */
+#ifndef _LINUX_MFD_STM32_TIMER_H_ +#define _LINUX_MFD_STM32_TIMER_H_
+#include <linux/clk.h> +#include <linux/mfd/core.h> +#include <linux/regmap.h> +#include <linux/reset.h>
+#define TIM_CR1 0x00 /* Control Register 1 */ +#define TIM_CR2 0x04 /* Control Register 2 */ +#define TIM_SMCR 0x08 /* Slave mode control reg */ +#define TIM_DIER 0x0C /* DMA/interrupt register */ +#define TIM_SR 0x10 /* Status register */ +#define TIM_EGR 0x14 /* Event Generation Reg */ +#define TIM_CCMR1 0x18 /* Capt/Comp 1 Mode Reg */ +#define TIM_CCMR2 0x1C /* Capt/Comp 2 Mode Reg */ +#define TIM_CCER 0x20 /* Capt/Comp Enable Reg */ +#define TIM_PSC 0x28 /* Prescaler */ +#define TIM_ARR 0x2c /* Auto-Reload Register */ +#define TIM_CCR1 0x34 /* Capt/Comp Register 1 */ +#define TIM_CCR2 0x38 /* Capt/Comp Register 2 */ +#define TIM_CCR3 0x3C /* Capt/Comp Register 3 */ +#define TIM_CCR4 0x40 /* Capt/Comp Register 4 */ +#define TIM_BDTR 0x44 /* Break and Dead-Time Reg */
+#define TIM_CR1_CEN BIT(0) /* Counter Enable */ +#define TIM_CR1_ARPE BIT(7) /* Auto-reload Preload Ena */ +#define TIM_CR2_MMS (BIT(4) | BIT(5) | BIT(6)) /* Master mode selection */ +#define TIM_SMCR_SMS (BIT(0) | BIT(1) | BIT(2)) /* Slave mode selection */ +#define TIM_SMCR_TS (BIT(4) | BIT(5) | BIT(6)) /* Trigger selection */ +#define TIM_DIER_UIE BIT(0) /* Update interrupt */ +#define TIM_SR_UIF BIT(0) /* Update interrupt flag */ +#define TIM_EGR_UG BIT(0) /* Update Generation */ +#define TIM_CCMR_PE BIT(3) /* Channel Preload Enable */ +#define TIM_CCMR_M1 (BIT(6) | BIT(5)) /* Channel PWM Mode 1 */ +#define TIM_CCER_CC1E BIT(0) /* Capt/Comp 1 out Ena */ +#define TIM_CCER_CC1P BIT(1) /* Capt/Comp 1 Polarity */ +#define TIM_CCER_CC1NE BIT(2) /* Capt/Comp 1N out Ena */ +#define TIM_CCER_CC1NP BIT(3) /* Capt/Comp 1N Polarity */ +#define TIM_CCER_CCXE (BIT(0) | BIT(4) | BIT(8) | BIT(12)) +#define TIM_BDTR_BKE BIT(12) /* Break input enable */ +#define TIM_BDTR_BKP BIT(13) /* Break input polarity */ +#define TIM_BDTR_AOE BIT(14) /* Automatic Output Enable */ +#define TIM_BDTR_MOE BIT(15) /* Main Output Enable */
+#define STM32_TIMER_CELLS 2 +#define MAX_TIM_PSC 0xFFFF
+struct stm32_mfd_timer_cfg {
- const char *pwm_name;
- const char *pwm_compatible;
- const char *trigger_name;
- const char *trigger_compatible;
+};
+struct stm32_mfd_timer_dev {
- /* Device data */
- struct device *dev;
- struct clk *clk;
- int irq;
- /* Registers mapping */
- struct regmap *regmap;
- /* Private data */
- struct mfd_cell cells[STM32_TIMER_CELLS];
- struct stm32_mfd_timer_cfg *cfg;
+};
+#endif
Your comments are welcome on all of them ;-)
2016-11-22 17:41 GMT+01:00 Lee Jones lee.jones@linaro.org:
On Tue, 22 Nov 2016, Lee Jones wrote:
On Tue, 22 Nov 2016, Benjamin Gaignard wrote:
This hardware block could at used at same time for PWM generation and IIO timer for other IPs like DAC, ADC or other timers. PWM and IIO timer configuration are mixed in the same registers so we need a MFD to be able to share those registers.
Signed-off-by: Benjamin Gaignard benjamin.gaignard@st.com
drivers/mfd/Kconfig | 10 ++ drivers/mfd/Makefile | 2 + drivers/mfd/stm32-mfd-timer.c | 236 ++++++++++++++++++++++++++++++++++++ include/linux/mfd/stm32-mfd-timer.h | 78 ++++++++++++ 4 files changed, 326 insertions(+) create mode 100644 drivers/mfd/stm32-mfd-timer.c create mode 100644 include/linux/mfd/stm32-mfd-timer.h
This driver is going to need a re-write.
However, it's difficult to provide suggestions, since I've been left off of the Cc: list for all the other patches.
Please re-send the set with all of the Maintainers Cc'ed on all of the patches.
Scrap that -- they all just came trickling through!
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index c6df644..63aee36 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1607,6 +1607,15 @@ config MFD_STW481X in various ST Microelectronics and ST-Ericsson embedded Nomadik series.
+config MFD_STM32_TIMER
- tristate "Support for STM32 multifunctions timer"
- select MFD_CORE
- select REGMAP
- depends on ARCH_STM32
- depends on OF
- help
Select multifunction driver (pwm, IIO trigger) for stm32 timers
menu "Multimedia Capabilities Port drivers" depends on ARCH_SA1100
@@ -1644,4 +1653,5 @@ config MFD_VEXPRESS_SYSREG on the ARM Ltd. Versatile Express board.
endmenu
endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 9834e66..b348c3e 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -211,3 +211,5 @@ obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o obj-$(CONFIG_MFD_MT6397) += mt6397-core.o
obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o
+obj-$(CONFIG_MFD_STM32_TIMER) += stm32-mfd-timer.o diff --git a/drivers/mfd/stm32-mfd-timer.c b/drivers/mfd/stm32-mfd-timer.c new file mode 100644 index 0000000..67e7db3 --- /dev/null +++ b/drivers/mfd/stm32-mfd-timer.c @@ -0,0 +1,236 @@ +/*
- stm32-timer.c
- Copyright (C) STMicroelectronics 2016
- Author: Benjamin Gaignard benjamin.gaignard@st.com for STMicroelectronics.
- License terms: GNU General Public License (GPL), version 2
- */
+#include <linux/device.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/of.h>
+#include <linux/mfd/stm32-mfd-timer.h>
+static const struct stm32_mfd_timer_cfg mfd_cells_cfg[] = {
- {
.pwm_name = "pwm1",
.pwm_compatible = "st,stm32-pwm1",
.trigger_name = "iiotimer1",
.trigger_compatible = "st,stm32-iio-timer1",
- },
- {
.pwm_name = "pwm2",
.pwm_compatible = "st,stm32-pwm2",
.trigger_name = "iiotimer2",
.trigger_compatible = "st,stm32-iio-timer2",
- },
- {
.pwm_name = "pwm3",
.pwm_compatible = "st,stm32-pwm3",
.trigger_name = "iiotimer3",
.trigger_compatible = "st,stm32-iio-timer3",
- },
- {
.pwm_name = "pwm4",
.pwm_compatible = "st,stm32-pwm4",
.trigger_name = "iiotimer4",
.trigger_compatible = "st,stm32-iio-timer4",
- },
- {
.pwm_name = "pwm5",
.pwm_compatible = "st,stm32-pwm5",
.trigger_name = "iiotimer5",
.trigger_compatible = "st,stm32-iio-timer5",
- },
- {
.trigger_name = "iiotimer6",
.trigger_compatible = "st,stm32-iio-timer6",
- },
- {
.trigger_name = "iiotimer7",
.trigger_compatible = "st,stm32-iio-timer7",
- },
- {
.pwm_name = "pwm8",
.pwm_compatible = "st,stm32-pwm8",
.trigger_name = "iiotimer8",
.trigger_compatible = "st,stm32-iio-timer8",
- },
- {
.pwm_name = "pwm9",
.pwm_compatible = "st,stm32-pwm9",
.trigger_name = "iiotimer9",
.trigger_compatible = "st,stm32-iio-timer9",
- },
- {
.pwm_name = "pwm10",
.pwm_compatible = "st,stm32-pwm10",
- },
- {
.pwm_name = "pwm11",
.pwm_compatible = "st,stm32-pwm11",
- },
- {
.pwm_name = "pwm12",
.pwm_compatible = "st,stm32-pwm12",
.trigger_name = "iiotimer12",
.trigger_compatible = "st,stm32-iio-timer12",
- },
- {
.pwm_name = "pwm13",
.pwm_compatible = "st,stm32-pwm13",
- },
- {
.pwm_name = "pwm14",
.pwm_compatible = "st,stm32-pwm14",
- },
+};
+static const struct of_device_id stm32_timer_of_match[] = {
- {
.compatible = "st,stm32-mfd-timer1",
.data = &mfd_cells_cfg[0],
- },
- {
.compatible = "st,stm32-mfd-timer2",
.data = &mfd_cells_cfg[1],
- },
- {
.compatible = "st,stm32-mfd-timer3",
.data = &mfd_cells_cfg[2],
- },
- {
.compatible = "st,stm32-mfd-timer4",
.data = &mfd_cells_cfg[3],
- },
- {
.compatible = "st,stm32-mfd-timer5",
.data = &mfd_cells_cfg[4],
- },
- {
.compatible = "st,stm32-mfd-timer6",
.data = &mfd_cells_cfg[5],
- },
- {
.compatible = "st,stm32-mfd-timer7",
.data = &mfd_cells_cfg[6],
- },
- {
.compatible = "st,stm32-mfd-timer8",
.data = &mfd_cells_cfg[7],
- },
- {
.compatible = "st,stm32-mfd-timer9",
.data = &mfd_cells_cfg[8],
- },
- {
.compatible = "st,stm32-mfd-timer10",
.data = &mfd_cells_cfg[9],
- },
- {
.compatible = "st,stm32-mfd-timer11",
.data = &mfd_cells_cfg[10],
- },
- {
.compatible = "st,stm32-mfd-timer12",
.data = &mfd_cells_cfg[11],
- },
- {
.compatible = "st,stm32-mfd-timer13",
.data = &mfd_cells_cfg[12],
- },
- {
.compatible = "st,stm32-mfd-timer14",
.data = &mfd_cells_cfg[13],
- },
+};
+static const struct regmap_config stm32_timer_regmap_cfg = {
- .reg_bits = 32,
- .val_bits = 32,
- .reg_stride = sizeof(u32),
- .max_register = 0x400,
- .fast_io = true,
+};
+static int stm32_mfd_timer_probe(struct platform_device *pdev) +{
- struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
- struct stm32_mfd_timer_dev *mfd;
- struct resource *res;
- int ret, nb_cells = 0;
- struct mfd_cell *cell = NULL;
- void __iomem *mmio;
- mfd = devm_kzalloc(dev, sizeof(*mfd), GFP_KERNEL);
- if (!mfd)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
return -ENOMEM;
- mmio = devm_ioremap_resource(dev, res);
- if (IS_ERR(mmio))
return PTR_ERR(mmio);
- mfd->regmap = devm_regmap_init_mmio_clk(dev, "mfd_timer_clk", mmio,
&stm32_timer_regmap_cfg);
- if (IS_ERR(mfd->regmap))
return PTR_ERR(mfd->regmap);
- mfd->clk = devm_clk_get(dev, NULL);
- if (IS_ERR(mfd->clk))
return PTR_ERR(mfd->clk);
- mfd->irq = platform_get_irq(pdev, 0);
- if (mfd->irq < 0)
return -EINVAL;
- /* populate data structure depending on compatibility */
- if (!of_match_node(stm32_timer_of_match, np)->data)
return -EINVAL;
- mfd->cfg =
- (struct stm32_mfd_timer_cfg *)of_match_node(stm32_timer_of_match, np)->data;
- if (mfd->cfg->pwm_name && mfd->cfg->pwm_compatible) {
cell = &mfd->cells[nb_cells++];
cell->name = mfd->cfg->pwm_name;
cell->of_compatible = mfd->cfg->pwm_compatible;
cell->platform_data = mfd;
cell->pdata_size = sizeof(*mfd);
- }
- if (mfd->cfg->trigger_name && mfd->cfg->trigger_compatible) {
cell = &mfd->cells[nb_cells++];
cell->name = mfd->cfg->trigger_name;
cell->of_compatible = mfd->cfg->trigger_compatible;
cell->platform_data = mfd;
cell->pdata_size = sizeof(*mfd);
- }
- ret = devm_mfd_add_devices(&pdev->dev, pdev->id, mfd->cells,
nb_cells, NULL, 0, NULL);
- if (ret)
return ret;
- platform_set_drvdata(pdev, mfd);
- return 0;
+}
+static struct platform_driver stm32_mfd_timer_driver = {
- .probe = stm32_mfd_timer_probe,
- .driver = {
.name = "stm32-mfd-timer",
.of_match_table = stm32_timer_of_match,
- },
+}; +module_platform_driver(stm32_mfd_timer_driver);
+MODULE_DESCRIPTION("STMicroelectronics STM32 Timer MFD"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/stm32-mfd-timer.h b/include/linux/mfd/stm32-mfd-timer.h new file mode 100644 index 0000000..4a79c22 --- /dev/null +++ b/include/linux/mfd/stm32-mfd-timer.h @@ -0,0 +1,78 @@ +/*
- stm32-mfd-timer.h
- Copyright (C) STMicroelectronics 2016
- Author: Benjamin Gaignard benjamin.gaignard@st.com for STMicroelectronics.
- License terms: GNU General Public License (GPL), version 2
- */
+#ifndef _LINUX_MFD_STM32_TIMER_H_ +#define _LINUX_MFD_STM32_TIMER_H_
+#include <linux/clk.h> +#include <linux/mfd/core.h> +#include <linux/regmap.h> +#include <linux/reset.h>
+#define TIM_CR1 0x00 /* Control Register 1 */ +#define TIM_CR2 0x04 /* Control Register 2 */ +#define TIM_SMCR 0x08 /* Slave mode control reg */ +#define TIM_DIER 0x0C /* DMA/interrupt register */ +#define TIM_SR 0x10 /* Status register */ +#define TIM_EGR 0x14 /* Event Generation Reg */ +#define TIM_CCMR1 0x18 /* Capt/Comp 1 Mode Reg */ +#define TIM_CCMR2 0x1C /* Capt/Comp 2 Mode Reg */ +#define TIM_CCER 0x20 /* Capt/Comp Enable Reg */ +#define TIM_PSC 0x28 /* Prescaler */ +#define TIM_ARR 0x2c /* Auto-Reload Register */ +#define TIM_CCR1 0x34 /* Capt/Comp Register 1 */ +#define TIM_CCR2 0x38 /* Capt/Comp Register 2 */ +#define TIM_CCR3 0x3C /* Capt/Comp Register 3 */ +#define TIM_CCR4 0x40 /* Capt/Comp Register 4 */ +#define TIM_BDTR 0x44 /* Break and Dead-Time Reg */
+#define TIM_CR1_CEN BIT(0) /* Counter Enable */ +#define TIM_CR1_ARPE BIT(7) /* Auto-reload Preload Ena */ +#define TIM_CR2_MMS (BIT(4) | BIT(5) | BIT(6)) /* Master mode selection */ +#define TIM_SMCR_SMS (BIT(0) | BIT(1) | BIT(2)) /* Slave mode selection */ +#define TIM_SMCR_TS (BIT(4) | BIT(5) | BIT(6)) /* Trigger selection */ +#define TIM_DIER_UIE BIT(0) /* Update interrupt */ +#define TIM_SR_UIF BIT(0) /* Update interrupt flag */ +#define TIM_EGR_UG BIT(0) /* Update Generation */ +#define TIM_CCMR_PE BIT(3) /* Channel Preload Enable */ +#define TIM_CCMR_M1 (BIT(6) | BIT(5)) /* Channel PWM Mode 1 */ +#define TIM_CCER_CC1E BIT(0) /* Capt/Comp 1 out Ena */ +#define TIM_CCER_CC1P BIT(1) /* Capt/Comp 1 Polarity */ +#define TIM_CCER_CC1NE BIT(2) /* Capt/Comp 1N out Ena */ +#define TIM_CCER_CC1NP BIT(3) /* Capt/Comp 1N Polarity */ +#define TIM_CCER_CCXE (BIT(0) | BIT(4) | BIT(8) | BIT(12)) +#define TIM_BDTR_BKE BIT(12) /* Break input enable */ +#define TIM_BDTR_BKP BIT(13) /* Break input polarity */ +#define TIM_BDTR_AOE BIT(14) /* Automatic Output Enable */ +#define TIM_BDTR_MOE BIT(15) /* Main Output Enable */
+#define STM32_TIMER_CELLS 2 +#define MAX_TIM_PSC 0xFFFF
+struct stm32_mfd_timer_cfg {
- const char *pwm_name;
- const char *pwm_compatible;
- const char *trigger_name;
- const char *trigger_compatible;
+};
+struct stm32_mfd_timer_dev {
- /* Device data */
- struct device *dev;
- struct clk *clk;
- int irq;
- /* Registers mapping */
- struct regmap *regmap;
- /* Private data */
- struct mfd_cell cells[STM32_TIMER_CELLS];
- struct stm32_mfd_timer_cfg *cfg;
+};
+#endif
-- Lee Jones Linaro STMicroelectronics Landing Team Lead Linaro.org │ Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog
Define binding for pwm-stm32
Signed-off-by: Benjamin Gaignard benjamin.gaignard@st.com --- .../devicetree/bindings/pwm/pwm-stm32.txt | 43 ++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/pwm-stm32.txt
diff --git a/Documentation/devicetree/bindings/pwm/pwm-stm32.txt b/Documentation/devicetree/bindings/pwm/pwm-stm32.txt new file mode 100644 index 0000000..819e024 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-stm32.txt @@ -0,0 +1,43 @@ +STMicroelectronics PWM driver bindings for STM32 +-------------------------------------- + +Must be a child of STM32 multifunctions timer driver + +Required parameters: +- compatible : "st,stm32-pwm1" + "st,stm32-pwm2" + "st,stm32-pwm3" + "st,stm32-pwm4" + "st,stm32-pwm5" + "st,stm32-pwm8" + "st,stm32-pwm9" + "st,stm32-pwm10" + "st,stm32-pwm11" + "st,stm32-pwm12" + "st,stm32-pwm13" + "st,stm32-pwm14" +- pinctrl-names: Set to "default". +- pinctrl-0: List of phandles pointing to pin configuration nodes + for PWM module. + For Pinctrl properties, please refer to [1]. + +Optional parameters: +- st,breakinput-polarity if set enable break input feature. + The value define the active polarity: + - 0 (active LOW) + - 1 (active HIGH) + +[1] Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt + +Example: + mfd_timer1: mfdtimer1@40010000 { + compatible = "st,stm32-mfd-timer1"; + reg = <0x40010000 0x400>; + clocks = <&rcc 0 160>; + clock-names = "mfd_timer_clk"; + interrupts = <27>; + + pwm1: pwm1@40010000 { + compatible = "st,stm32-pwm1"; + }; + };
This driver add support for pwm driver on stm32 platform. The SoC have multiple instances of the hardware IP and each of them could have small differences: number of channels, complementary output, counter register size... To handle those variations each block have its own compatible linked to internal table that describe them.
Signed-off-by: Benjamin Gaignard benjamin.gaignard@st.com --- drivers/pwm/Kconfig | 8 ++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-stm32.c | 358 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 367 insertions(+) create mode 100644 drivers/pwm/pwm-stm32.c
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index bf01288..aeee045 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -388,6 +388,14 @@ config PWM_STI To compile this driver as a module, choose M here: the module will be called pwm-sti.
+config PWM_STM32 + bool "STMicroelectronics STM32 PWM" + depends on ARCH_STM32 + depends on OF + select MFD_STM32_TIMER + help + Generic PWM framework driver for STM32 SoCs. + config PWM_STMPE bool "STMPE expander PWM export" depends on MFD_STMPE diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 1194c54..5aa9308 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o 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_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.c b/drivers/pwm/pwm-stm32.c new file mode 100644 index 0000000..2d71ee7 --- /dev/null +++ b/drivers/pwm/pwm-stm32.c @@ -0,0 +1,358 @@ +/* + * Copyright (C) STMicroelectronics 2016 + * Author: Gerald Baeza gerald.baeza@st.com + * License terms: GNU General Public License (GPL), version 2 + * + * Inspired by timer-stm32.c from Maxime Coquelin + * pwm-atmel.c from Bo Shen + */ + +#include <linux/module.h> +#include <linux/pwm.h> + +#include <linux/mfd/stm32-mfd-timer.h> + +#define DRIVER_NAME "stm32-pwm" + +#define CAP_COMPLEMENTARY BIT(0) +#define CAP_32BIT_COUNTER BIT(1) +#define CAP_BREAKINPUT BIT(2) + +struct stm32_pwm_cfg { + int npwm; + int caps; +}; + +static struct stm32_pwm_cfg f4_pwm_cfg[] = { + /* for pwm 1 and 8 */ + { + .npwm = 4, + .caps = CAP_COMPLEMENTARY | CAP_BREAKINPUT, + }, + /* for pwm 2 and 5 */ + { + .npwm = 4, + .caps = CAP_32BIT_COUNTER, + }, + /* for pwm 3 and 4 */ + { + .npwm = 4, + .caps = 0, + }, + /* for pwm 9 and 12 */ + { + .npwm = 2, + .caps = 0, + }, + /* for pwm 10, 11, 13 and 14 */ + { + .npwm = 1, + .caps = 0, + }, +}; + +struct stm32_pwm_dev { + struct device *dev; + struct clk *clk; + struct regmap *regmap; + struct pwm_chip chip; + struct stm32_pwm_cfg *cfg; + bool have_breakinput; + u32 breakinput_polarity; +}; + +#define to_stm32_pwm_dev(x) container_of(chip, struct stm32_pwm_dev, chip) + +static u32 __active_channels(struct stm32_pwm_dev *pwm_dev) +{ + u32 ccer; + + regmap_read(pwm_dev->regmap, TIM_CCER, &ccer); + + return ccer & TIM_CCER_CCXE; +} + +static int write_ccrx(struct stm32_pwm_dev *dev, struct pwm_device *pwm, + u32 ccr) +{ + switch (pwm->hwpwm) { + case 0: + return regmap_write(dev->regmap, TIM_CCR1, ccr); + case 1: + return regmap_write(dev->regmap, TIM_CCR2, ccr); + case 2: + return regmap_write(dev->regmap, TIM_CCR3, ccr); + case 3: + return regmap_write(dev->regmap, TIM_CCR4, ccr); + } + return -EINVAL; +} + +static int stm32_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct stm32_pwm_dev *dev = to_stm32_pwm_dev(chip); + unsigned long long prd, div, dty; + int prescaler = 0; + u32 max_arr = 0xFFFF, ccmr, mask, shift, bdtr; + + if (dev->cfg->caps & CAP_32BIT_COUNTER) + max_arr = 0xFFFFFFFF; + + /* Period and prescaler values depends of clock rate */ + div = (unsigned long long)clk_get_rate(dev->clk) * period_ns; + + do_div(div, NSEC_PER_SEC); + prd = div; + + while (div > max_arr) { + prescaler++; + div = prd; + do_div(div, (prescaler + 1)); + } + prd = div; + + if (prescaler > MAX_TIM_PSC) { + dev_err(chip->dev, "prescaler exceeds the maximum value\n"); + return -EINVAL; + } + + /* All channels share the same prescaler and counter so + * when two channels are active at the same we can't change them + */ + if (__active_channels(dev) & ~(1 << pwm->hwpwm * 4)) { + u32 psc, arr; + + regmap_read(dev->regmap, TIM_PSC, &psc); + regmap_read(dev->regmap, TIM_ARR, &arr); + + if ((psc != prescaler) || (arr != prd - 1)) + return -EINVAL; + } + + regmap_write(dev->regmap, TIM_PSC, prescaler); + regmap_write(dev->regmap, TIM_ARR, prd - 1); + regmap_update_bits(dev->regmap, TIM_CR1, TIM_CR1_ARPE, TIM_CR1_ARPE); + + /* Calculate the duty cycles */ + dty = prd * duty_ns; + do_div(dty, period_ns); + + write_ccrx(dev, pwm, dty); + + /* Configure output mode */ + shift = (pwm->hwpwm & 0x1) * 8; + ccmr = (TIM_CCMR_PE | TIM_CCMR_M1) << shift; + mask = 0xFF << shift; + + if (pwm->hwpwm & 0x2) + regmap_update_bits(dev->regmap, TIM_CCMR2, mask, ccmr); + else + regmap_update_bits(dev->regmap, TIM_CCMR1, mask, ccmr); + + bdtr = TIM_BDTR_MOE | TIM_BDTR_AOE; + if (dev->have_breakinput) { + bdtr |= TIM_BDTR_BKE; + if (dev->breakinput_polarity) + bdtr |= TIM_BDTR_BKP; + } + + regmap_update_bits(dev->regmap, TIM_BDTR, + TIM_BDTR_MOE | TIM_BDTR_AOE | + TIM_BDTR_BKP | TIM_BDTR_BKE, + bdtr); + + return 0; +} + +static int stm32_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, + enum pwm_polarity polarity) +{ + u32 mask; + struct stm32_pwm_dev *dev = to_stm32_pwm_dev(chip); + + mask = TIM_CCER_CC1P << (pwm->hwpwm * 4); + if (dev->cfg->caps & CAP_COMPLEMENTARY) + mask |= TIM_CCER_CC1NP << (pwm->hwpwm * 4); + + regmap_update_bits(dev->regmap, TIM_CCER, mask, + polarity == PWM_POLARITY_NORMAL ? 0 : mask); + + return 0; +} + +static int stm32_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + u32 mask; + struct stm32_pwm_dev *dev = to_stm32_pwm_dev(chip); + + clk_enable(dev->clk); + + /* Enable channel */ + mask = TIM_CCER_CC1E << (pwm->hwpwm * 4); + if (dev->cfg->caps & CAP_COMPLEMENTARY) + mask |= TIM_CCER_CC1NE << (pwm->hwpwm * 4); + + regmap_update_bits(dev->regmap, TIM_CCER, mask, mask); + + /* Make sure that registers are updated */ + regmap_update_bits(dev->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG); + + /* Enable controller */ + regmap_update_bits(dev->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN); + + return 0; +} + +static void stm32_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + u32 mask; + struct stm32_pwm_dev *dev = to_stm32_pwm_dev(chip); + + /* Disable channel */ + mask = TIM_CCER_CC1E << (pwm->hwpwm * 4); + if (dev->cfg->caps & CAP_COMPLEMENTARY) + mask |= TIM_CCER_CC1NE << (pwm->hwpwm * 4); + + regmap_update_bits(dev->regmap, TIM_CCER, mask, 0); + + /* When all channels are disabled, we can disable the controller */ + if (!__active_channels(dev)) + regmap_update_bits(dev->regmap, TIM_CR1, TIM_CR1_CEN, 0); + + clk_disable(dev->clk); +} + +static const struct pwm_ops stm32pwm_ops = { + .config = stm32_pwm_config, + .set_polarity = stm32_pwm_set_polarity, + .enable = stm32_pwm_enable, + .disable = stm32_pwm_disable, +}; + +static const struct of_device_id stm32_pwm_of_match[] = { + { + .compatible = "st,stm32-pwm1", + .data = &f4_pwm_cfg[0], + }, + { + .compatible = "st,stm32-pwm2", + .data = &f4_pwm_cfg[1], + }, + { + .compatible = "st,stm32-pwm3", + .data = &f4_pwm_cfg[2], + }, + { + .compatible = "st,stm32-pwm4", + .data = &f4_pwm_cfg[2], + }, + { + .compatible = "st,stm32-pwm5", + .data = &f4_pwm_cfg[1], + }, + { + .compatible = "st,stm32-pwm8", + .data = &f4_pwm_cfg[0], + }, + { + .compatible = "st,stm32-pwm9", + .data = &f4_pwm_cfg[3], + }, + { + .compatible = "st,stm32-pwm10", + .data = &f4_pwm_cfg[4], + }, + { + .compatible = "st,stm32-pwm11", + .data = &f4_pwm_cfg[4], + }, + { + .compatible = "st,stm32-pwm12", + .data = &f4_pwm_cfg[3], + }, + { + .compatible = "st,stm32-pwm13", + .data = &f4_pwm_cfg[4], + }, + { + .compatible = "st,stm32-pwm14", + .data = &f4_pwm_cfg[4], + }, + { + /* end node */ + }, +}; +MODULE_DEVICE_TABLE(of, stm32_pwm_of_match); + +static int stm32_pwm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct stm32_mfd_timer_dev *mfd = pdev->dev.platform_data; + struct stm32_pwm_dev *pwm; + int ret; + + pwm = devm_kzalloc(dev, sizeof(*pwm), GFP_KERNEL); + if (!pwm) + return -ENOMEM; + + pwm->regmap = mfd->regmap; + pwm->clk = mfd->clk; + + if (!pwm->regmap || !pwm->clk) + return -EINVAL; + + /* populate data structure depending on compatibility */ + if (!of_match_node(stm32_pwm_of_match, np)->data) + return -EINVAL; + + pwm->cfg = + (struct stm32_pwm_cfg *)of_match_node(stm32_pwm_of_match, np)->data; + + if (pwm->cfg->caps & CAP_BREAKINPUT) { + if (!of_property_read_u32(np, "st,breakinput-polarity", + &pwm->breakinput_polarity)) + pwm->have_breakinput = true; + } + + pwm->chip.base = -1; + pwm->chip.dev = dev; + pwm->chip.ops = &stm32pwm_ops; + pwm->chip.npwm = pwm->cfg->npwm; + + ret = pwmchip_add(&pwm->chip); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, pwm); + + return 0; +} + +static int stm32_pwm_remove(struct platform_device *pdev) +{ + struct stm32_pwm_dev *pwm = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < pwm->cfg->npwm; i++) + pwm_disable(&pwm->chip.pwms[i]); + + pwmchip_remove(&pwm->chip); + + return 0; +} + +static struct platform_driver stm32_pwm_driver = { + .probe = stm32_pwm_probe, + .remove = stm32_pwm_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = stm32_pwm_of_match, + }, +}; +module_platform_driver(stm32_pwm_driver); + +MODULE_ALIAS("platform:" DRIVER_NAME); +MODULE_DESCRIPTION("STMicroelectronics STM32 PWM driver"); +MODULE_LICENSE("GPL");
Define bindings for stm32 IIO timer
Signed-off-by: Benjamin Gaignard benjamin.gaignard@st.com --- .../bindings/iio/timer/stm32-iio-timer.txt | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/timer/stm32-iio-timer.txt
diff --git a/Documentation/devicetree/bindings/iio/timer/stm32-iio-timer.txt b/Documentation/devicetree/bindings/iio/timer/stm32-iio-timer.txt new file mode 100644 index 0000000..b80025e --- /dev/null +++ b/Documentation/devicetree/bindings/iio/timer/stm32-iio-timer.txt @@ -0,0 +1,33 @@ +timer IIO trigger bindings for STM32 + +Must be a child of STM32 multifunctions timer driver + +Required parameters: +- compatible: must be one of the follow value: + "st,stm32-iio-timer1" + "st,stm32-iio-timer2" + "st,stm32-iio-timer3" + "st,stm32-iio-timer4" + "st,stm32-iio-timer5" + "st,stm32-iio-timer6" + "st,stm32-iio-timer7" + "st,stm32-iio-timer8" + "st,stm32-iio-timer9" + "st,stm32-iio-timer10" + "st,stm32-iio-timer11" + "st,stm32-iio-timer12" + "st,stm32-iio-timer13" + "st,stm32-iio-timer14" + +Example: + mfd_timer1: mfdtimer1@40010000 { + compatible = "st,stm32-mfd-timer1"; + reg = <0x40010000 0x400>; + clocks = <&rcc 0 160>; + clock-names = "mfd_timer_clk"; + interrupts = <27>; + + trigger1: trigger1@40010000 { + compatible = "st,stm32-iio-timer1"; + }; + };
On 11/22/2016 05:13 PM, Benjamin Gaignard wrote:
Define bindings for stm32 IIO timer
Signed-off-by: Benjamin Gaignard benjamin.gaignard@st.com
.../bindings/iio/timer/stm32-iio-timer.txt | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/timer/stm32-iio-timer.txt
diff --git a/Documentation/devicetree/bindings/iio/timer/stm32-iio-timer.txt b/Documentation/devicetree/bindings/iio/timer/stm32-iio-timer.txt new file mode 100644 index 0000000..b80025e --- /dev/null +++ b/Documentation/devicetree/bindings/iio/timer/stm32-iio-timer.txt @@ -0,0 +1,33 @@ +timer IIO trigger bindings for STM32
+Must be a child of STM32 multifunctions timer driver
+Required parameters: +- compatible: must be one of the follow value:
- "st,stm32-iio-timer1"
- "st,stm32-iio-timer2"
- "st,stm32-iio-timer3"
- "st,stm32-iio-timer4"
- "st,stm32-iio-timer5"
- "st,stm32-iio-timer6"
- "st,stm32-iio-timer7"
- "st,stm32-iio-timer8"
- "st,stm32-iio-timer9"
- "st,stm32-iio-timer10"
- "st,stm32-iio-timer11"
- "st,stm32-iio-timer12"
- "st,stm32-iio-timer13"
- "st,stm32-iio-timer14"
We can't do this. This is a binding for a driver, not for the hardware.
[snip]
"st,stm32-iio-timer5"
"st,stm32-iio-timer6"
"st,stm32-iio-timer7"
"st,stm32-iio-timer8"
"st,stm32-iio-timer9"
"st,stm32-iio-timer10"
"st,stm32-iio-timer11"
"st,stm32-iio-timer12"
"st,stm32-iio-timer13"
"st,stm32-iio-timer14"
We can't do this. This is a binding for a driver, not for the hardware.
Unfortunately each instance for the hardware IP have little differences like which triggers they could accept or size of the counter register, and I doesn't have value inside the hardware to distinguish them so the only way I found is to use compatible.
On 11/22/2016 06:01 PM, Benjamin Gaignard wrote:
[snip]
"st,stm32-iio-timer5"
"st,stm32-iio-timer6"
"st,stm32-iio-timer7"
"st,stm32-iio-timer8"
"st,stm32-iio-timer9"
"st,stm32-iio-timer10"
"st,stm32-iio-timer11"
"st,stm32-iio-timer12"
"st,stm32-iio-timer13"
"st,stm32-iio-timer14"
We can't do this. This is a binding for a driver, not for the hardware.
Unfortunately each instance for the hardware IP have little differences like which triggers they could accept or size of the counter register, and I doesn't have value inside the hardware to distinguish them so the only way I found is to use compatible.
But IIO is not a piece of hardware, its a software framework in the Linux kernel.
On Tue, 22 Nov 2016, Benjamin Gaignard wrote:
[snip]
"st,stm32-iio-timer5"
"st,stm32-iio-timer6"
"st,stm32-iio-timer7"
"st,stm32-iio-timer8"
"st,stm32-iio-timer9"
"st,stm32-iio-timer10"
"st,stm32-iio-timer11"
"st,stm32-iio-timer12"
"st,stm32-iio-timer13"
"st,stm32-iio-timer14"
We can't do this. This is a binding for a driver, not for the hardware.
Unfortunately each instance for the hardware IP have little differences like which triggers they could accept or size of the counter register, and I doesn't have value inside the hardware to distinguish them so the only way I found is to use compatible.
Can't you represent these as properties?
If it is ok for you I will add "id" parameter in mfd driver and forward it to the sub-devices drivers to be able to distinguish the hardware blocks
2016-11-22 18:18 GMT+01:00 Lee Jones lee.jones@linaro.org:
On Tue, 22 Nov 2016, Benjamin Gaignard wrote:
[snip]
"st,stm32-iio-timer5"
"st,stm32-iio-timer6"
"st,stm32-iio-timer7"
"st,stm32-iio-timer8"
"st,stm32-iio-timer9"
"st,stm32-iio-timer10"
"st,stm32-iio-timer11"
"st,stm32-iio-timer12"
"st,stm32-iio-timer13"
"st,stm32-iio-timer14"
We can't do this. This is a binding for a driver, not for the hardware.
Unfortunately each instance for the hardware IP have little differences like which triggers they could accept or size of the counter register, and I doesn't have value inside the hardware to distinguish them so the only way I found is to use compatible.
Can't you represent these as properties?
-- Lee Jones Linaro STMicroelectronics Landing Team Lead Linaro.org │ Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog
On Wed, Nov 23, 2016 at 09:17:58AM +0100, Benjamin Gaignard wrote:
If it is ok for you I will add "id" parameter in mfd driver and forward it to the sub-devices drivers to be able to distinguish the hardware blocks
Please don't top post.
No, it's not okay. If the counter sizes are different, then have a property for the counter size. Describe how they are different without numbering them. If you can't describe the differences, then it shouldn't matter which ones the OS picks to use.
2016-11-22 18:18 GMT+01:00 Lee Jones lee.jones@linaro.org:
On Tue, 22 Nov 2016, Benjamin Gaignard wrote:
[snip]
"st,stm32-iio-timer5"
"st,stm32-iio-timer6"
"st,stm32-iio-timer7"
"st,stm32-iio-timer8"
"st,stm32-iio-timer9"
"st,stm32-iio-timer10"
"st,stm32-iio-timer11"
"st,stm32-iio-timer12"
"st,stm32-iio-timer13"
"st,stm32-iio-timer14"
I doubt the h/w manual calls these "IIO timers".
We can't do this. This is a binding for a driver, not for the hardware.
Unfortunately each instance for the hardware IP have little differences like which triggers they could accept or size of the counter register, and I doesn't have value inside the hardware to distinguish them so the only way I found is to use compatible.
Can't you represent these as properties?
-- Lee Jones Linaro STMicroelectronics Landing Team Lead Linaro.org │ Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog
-- Benjamin Gaignard
Graphic Study Group
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
Timers IPs can be used to generate triggers for other IPs like DAC, ADC or other timers. Each trigger may result of timer internals signals like counter enable, reset or edge, this configuration could be done through "master_mode" device attribute.
A timer device could be triggered by other timers, we use the trigger name and is_stm32_iio_timer_trigger() function to distinguish them and configure IP input switch.
Timer may also decide on which event (edge, level) they could be activated by a trigger, this configuration is done by writing in "slave_mode" device attribute.
Since triggers could also be used by DAC or ADC their names are defined in include/linux/iio/timer/stm32-iio-timers.h so those IPs will be able to configure themselves in valid_trigger function
Trigger have a "sampling_frequency" attribute which allow to configure timer sampling frequency without using pwm interface
Signed-off-by: Benjamin Gaignard benjamin.gaignard@st.com --- drivers/iio/Kconfig | 2 +- drivers/iio/Makefile | 1 + drivers/iio/timer/Kconfig | 15 + drivers/iio/timer/Makefile | 1 + drivers/iio/timer/stm32-iio-timer.c | 766 +++++++++++++++++++++++++++++ drivers/iio/trigger/Kconfig | 1 - include/linux/iio/timer/stm32-iio-timers.h | 25 + 7 files changed, 809 insertions(+), 2 deletions(-) create mode 100644 drivers/iio/timer/Kconfig create mode 100644 drivers/iio/timer/Makefile create mode 100644 drivers/iio/timer/stm32-iio-timer.c create mode 100644 include/linux/iio/timer/stm32-iio-timers.h
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 6743b18..2de2a80 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -90,5 +90,5 @@ source "drivers/iio/potentiometer/Kconfig" source "drivers/iio/pressure/Kconfig" source "drivers/iio/proximity/Kconfig" source "drivers/iio/temperature/Kconfig" - +source "drivers/iio/timer/Kconfig" endif # IIO diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 87e4c43..b797c08 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -32,4 +32,5 @@ obj-y += potentiometer/ obj-y += pressure/ obj-y += proximity/ obj-y += temperature/ +obj-y += timer/ obj-y += trigger/ diff --git a/drivers/iio/timer/Kconfig b/drivers/iio/timer/Kconfig new file mode 100644 index 0000000..55764e8 --- /dev/null +++ b/drivers/iio/timer/Kconfig @@ -0,0 +1,15 @@ +# +# Timers drivers + +menu "Timers" + +config IIO_STM32_TIMER + tristate "stm32 iio timer" + depends on ARCH_STM32 + depends on OF + select IIO_TRIGGERED_EVENT + select MFD_STM32_TIMER + help + Select this option to enable stm32 timers hardware IPs + +endmenu diff --git a/drivers/iio/timer/Makefile b/drivers/iio/timer/Makefile new file mode 100644 index 0000000..a360c9f --- /dev/null +++ b/drivers/iio/timer/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_IIO_STM32_TIMER) += stm32-iio-timer.o diff --git a/drivers/iio/timer/stm32-iio-timer.c b/drivers/iio/timer/stm32-iio-timer.c new file mode 100644 index 0000000..a1d54c4 --- /dev/null +++ b/drivers/iio/timer/stm32-iio-timer.c @@ -0,0 +1,766 @@ +/* + * stm32-iio-timer.c + * + * Copyright (C) STMicroelectronics 2016 + * Author: Benjamin Gaignard benjamin.gaignard@st.com for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/timer/stm32-iio-timers.h> +#include <linux/iio/trigger.h> +#include <linux/iio/triggered_event.h> +#include <linux/interrupt.h> +#include <linux/mfd/stm32-mfd-timer.h> +#include <linux/module.h> + +#define DRIVER_NAME "stm32-iio-timer" + +struct stm32_trigger { + const char *name; +}; + +struct stm32_valid_trigger { + const char *name; + int ts_value; +}; + +struct stm32_trig_cfg { + const struct stm32_trigger *triggers; + int nb_triggers; + const struct stm32_valid_trigger *valids; + int nb_valids; +}; + +struct stm32_iio_timer_dev { + struct device *dev; + struct regmap *regmap; + struct clk *clk; + int irq; + struct stm32_trig_cfg *cfg; + bool own_timer; + unsigned int sampling_frequency; + struct iio_trigger *active_trigger; +}; + +static ssize_t _store_frequency(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_trigger *trig = to_iio_trigger(dev); + struct stm32_iio_timer_dev *stm32 = iio_trigger_get_drvdata(trig); + unsigned int freq; + int ret; + + ret = kstrtouint(buf, 10, &freq); + if (ret) + return ret; + + stm32->sampling_frequency = freq; + + return len; +} + +static ssize_t _read_frequency(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_trigger *trig = to_iio_trigger(dev); + struct stm32_iio_timer_dev *stm32 = iio_trigger_get_drvdata(trig); + unsigned long long freq = stm32->sampling_frequency; + u32 psc, arr, cr1; + + regmap_read(stm32->regmap, TIM_CR1, &cr1); + regmap_read(stm32->regmap, TIM_PSC, &psc); + regmap_read(stm32->regmap, TIM_ARR, &arr); + + if (psc && arr && (cr1 & TIM_CR1_CEN)) { + freq = (unsigned long long)clk_get_rate(stm32->clk); + do_div(freq, psc); + do_div(freq, arr); + } + + return sprintf(buf, "%d\n", (unsigned int) freq); +} + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, + _read_frequency, + _store_frequency); + +static struct attribute *stm32_trigger_attrs[] = { + &iio_dev_attr_sampling_frequency.dev_attr.attr, + NULL, +}; + +static const struct attribute_group stm32_trigger_attr_group = { + .attrs = stm32_trigger_attrs, +}; + +static const struct attribute_group *stm32_trigger_attr_groups[] = { + &stm32_trigger_attr_group, + NULL, +}; + +static +ssize_t _show_master_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct stm32_iio_timer_dev *stm32 = iio_priv(indio_dev); + u32 cr2; + + regmap_read(stm32->regmap, TIM_CR2, &cr2); + + return snprintf(buf, PAGE_SIZE, "%d\n", (cr2 >> 4) & 0x7); +} + +static +ssize_t _store_master_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct stm32_iio_timer_dev *stm32 = iio_priv(indio_dev); + u8 mode; + int ret; + + ret = kstrtou8(buf, 10, &mode); + if (ret) + return ret; + + if (mode > 0x7) + return -EINVAL; + + regmap_update_bits(stm32->regmap, TIM_CR2, TIM_CR2_MMS, mode << 4); + + return len; +} + +static IIO_DEVICE_ATTR(master_mode, S_IRUGO | S_IWUSR, + _show_master_mode, + _store_master_mode, + 0); + +static +ssize_t _show_slave_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct stm32_iio_timer_dev *stm32 = iio_priv(indio_dev); + u32 smcr; + + regmap_read(stm32->regmap, TIM_SMCR, &smcr); + + return snprintf(buf, PAGE_SIZE, "%d\n", smcr & 0x3); +} + +static +ssize_t _store_slave_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct stm32_iio_timer_dev *stm32 = iio_priv(indio_dev); + u8 mode; + int ret; + + ret = kstrtou8(buf, 10, &mode); + if (ret) + return ret; + + if (mode > 0x7) + return -EINVAL; + + regmap_update_bits(stm32->regmap, TIM_SMCR, TIM_SMCR_SMS, mode); + + return len; +} + +static IIO_DEVICE_ATTR(slave_mode, S_IRUGO | S_IWUSR, + _show_slave_mode, + _store_slave_mode, + 0); + +static struct attribute *stm32_timer_attrs[] = { + &iio_dev_attr_master_mode.dev_attr.attr, + &iio_dev_attr_slave_mode.dev_attr.attr, + NULL, +}; + +static const struct attribute_group stm32_timer_attr_group = { + .attrs = stm32_timer_attrs, +}; + +static const struct stm32_trigger trigger1[] = { + { + .name = TIM1_TRGO, + }, +}; + +static const struct stm32_valid_trigger valid1[] = { + { + .name = TIM5_TRGO, + .ts_value = 0, + }, + { + .name = TIM2_TRGO, + .ts_value = 1, + }, + { + .name = TIM3_TRGO, + .ts_value = 2, + }, + { + .name = TIM4_TRGO, + .ts_value = 3, + }, +}; + +static const struct stm32_trig_cfg trigger1_cfg = { + .triggers = trigger1, + .nb_triggers = ARRAY_SIZE(trigger1), + .valids = valid1, + .nb_valids = ARRAY_SIZE(valid1), +}; + +static const struct stm32_trigger trigger2[] = { + { + .name = TIM2_TRGO, + }, +}; + +static const struct stm32_valid_trigger valid2[] = { + { + .name = TIM1_TRGO, + .ts_value = 0, + }, + { + .name = TIM8_TRGO, + .ts_value = 1, + }, + { + .name = TIM3_TRGO, + .ts_value = 2, + }, + { + .name = TIM4_TRGO, + .ts_value = 3, + }, +}; + +static const struct stm32_trig_cfg trigger2_cfg = { + .triggers = trigger2, + .nb_triggers = ARRAY_SIZE(trigger2), + .valids = valid2, + .nb_valids = ARRAY_SIZE(valid2), +}; + +static const struct stm32_trigger trigger3[] = { + { + .name = TIM3_TRGO, + }, +}; + +static const struct stm32_valid_trigger valid3[] = { + { + .name = TIM1_TRGO, + .ts_value = 0, + }, + { + .name = TIM8_TRGO, + .ts_value = 1, + }, + { + .name = TIM5_TRGO, + .ts_value = 2, + }, + { + .name = TIM4_TRGO, + .ts_value = 3, + }, +}; + +static const struct stm32_trig_cfg trigger3_cfg = { + .triggers = trigger3, + .nb_triggers = ARRAY_SIZE(trigger3), + .valids = valid3, + .nb_valids = ARRAY_SIZE(valid3), +}; + +static const struct stm32_trigger trigger4[] = { + { + .name = TIM4_TRGO, + }, +}; + +static const struct stm32_valid_trigger valid4[] = { + { + .name = TIM1_TRGO, + .ts_value = 0, + }, + { + .name = TIM2_TRGO, + .ts_value = 1, + }, + { + .name = TIM3_TRGO, + .ts_value = 2, + }, + { + .name = TIM8_TRGO, + .ts_value = 3, + }, +}; + +static const struct stm32_trig_cfg trigger4_cfg = { + .triggers = trigger4, + .nb_triggers = ARRAY_SIZE(trigger4), + .valids = valid4, + .nb_valids = ARRAY_SIZE(valid4), +}; + +static const struct stm32_trigger trigger5[] = { + { + .name = TIM5_TRGO, + }, +}; + +static const struct stm32_valid_trigger valid5[] = { + { + .name = TIM2_TRGO, + .ts_value = 0, + }, + { + .name = TIM3_TRGO, + .ts_value = 1, + }, + { + .name = TIM4_TRGO, + .ts_value = 2, + }, + { + .name = TIM8_TRGO, + .ts_value = 3, + }, +}; + +static const struct stm32_trig_cfg trigger5_cfg = { + .triggers = trigger5, + .nb_triggers = ARRAY_SIZE(trigger5), + .valids = valid5, + .nb_valids = ARRAY_SIZE(valid5), +}; + +static const struct stm32_trigger trigger6[] = { + { + .name = TIM6_TRGO, + }, +}; + +static const struct stm32_trig_cfg trigger6_cfg = { + .triggers = trigger6, + .nb_triggers = ARRAY_SIZE(trigger6), + .nb_valids = 0, +}; + +static const struct stm32_trigger trigger7[] = { + { + .name = TIM7_TRGO, + }, +}; + +static const struct stm32_trig_cfg trigger7_cfg = { + .triggers = trigger7, + .nb_triggers = ARRAY_SIZE(trigger7), + .nb_valids = 0, +}; + +static const struct stm32_trigger trigger8[] = { + { + .name = TIM8_TRGO, + }, +}; + +static const struct stm32_valid_trigger valid8[] = { + { + .name = TIM1_TRGO, + .ts_value = 0, + }, + { + .name = TIM2_TRGO, + .ts_value = 1, + }, + { + .name = TIM4_TRGO, + .ts_value = 2, + }, + { + .name = TIM5_TRGO, + .ts_value = 3, + }, +}; + +static const struct stm32_trig_cfg trigger8_cfg = { + .triggers = trigger8, + .nb_triggers = ARRAY_SIZE(trigger8), + .valids = valid8, + .nb_valids = ARRAY_SIZE(valid8), +}; + +static const struct stm32_trigger trigger9[] = { + { + .name = TIM9_TRGO, + }, +}; + +static const struct stm32_valid_trigger valid9[] = { + { + .name = TIM2_TRGO, + .ts_value = 0, + }, + { + .name = TIM3_TRGO, + .ts_value = 1, + }, +}; + +static const struct stm32_trig_cfg trigger9_cfg = { + .triggers = trigger9, + .nb_triggers = ARRAY_SIZE(trigger9), + .valids = valid9, + .nb_valids = ARRAY_SIZE(valid9), +}; + +static const struct stm32_trigger trigger12[] = { + { + .name = TIM12_TRGO, + }, +}; + +static const struct stm32_valid_trigger valid12[] = { + { + .name = TIM4_TRGO, + .ts_value = 0, + }, + { + .name = TIM5_TRGO, + .ts_value = 1, + }, +}; + +static const struct stm32_trig_cfg trigger12_cfg = { + .triggers = trigger12, + .nb_triggers = ARRAY_SIZE(trigger12), + .valids = valid12, + .nb_valids = ARRAY_SIZE(valid12), +}; + +static const struct of_device_id stm32_trig_of_match[] = { + { + .compatible = "st,stm32-iio-timer1", + .data = &trigger1_cfg, + }, + { + .compatible = "st,stm32-iio-timer2", + .data = &trigger2_cfg, + }, + { + .compatible = "st,stm32-iio-timer3", + .data = &trigger3_cfg, + }, + { + .compatible = "st,stm32-iio-timer4", + .data = &trigger4_cfg, + }, + { + .compatible = "st,stm32-iio-timer5", + .data = &trigger5_cfg, + }, + { + .compatible = "st,stm32-iio-timer6", + .data = &trigger6_cfg, + }, + { + .compatible = "st,stm32-iio-timer7", + .data = &trigger7_cfg, + }, + { + .compatible = "st,stm32-iio-timer8", + .data = &trigger8_cfg, + }, + { + .compatible = "st,stm32-iio-timer9", + .data = &trigger9_cfg, + }, + { + .compatible = "st,stm32-iio-timer12", + .data = &trigger12_cfg, + }, +}; +MODULE_DEVICE_TABLE(of, stm32_trig_of_match); + +static int stm32_timer_start(struct stm32_iio_timer_dev *stm32) +{ + unsigned long long prd, div; + int prescaler = 0; + u32 max_arr = 0xFFFF, cr1; + + if (stm32->sampling_frequency == 0) + return 0; + + /* Period and prescaler values depends of clock rate */ + div = (unsigned long long)clk_get_rate(stm32->clk); + + do_div(div, stm32->sampling_frequency); + + prd = div; + + while (div > max_arr) { + prescaler++; + div = prd; + do_div(div, (prescaler + 1)); + } + prd = div; + + if (prescaler > MAX_TIM_PSC) { + dev_err(stm32->dev, "prescaler exceeds the maximum value\n"); + return -EINVAL; + } + + /* Check that we own the timer */ + regmap_read(stm32->regmap, TIM_CR1, &cr1); + if ((cr1 & TIM_CR1_CEN) && !stm32->own_timer) + return -EBUSY; + + if (!stm32->own_timer) { + stm32->own_timer = true; + clk_enable(stm32->clk); + } + + regmap_write(stm32->regmap, TIM_PSC, prescaler); + regmap_write(stm32->regmap, TIM_ARR, prd - 1); + regmap_update_bits(stm32->regmap, TIM_CR1, TIM_CR1_ARPE, TIM_CR1_ARPE); + + /* Force master mode to update mode */ + regmap_update_bits(stm32->regmap, TIM_CR2, TIM_CR2_MMS, 0x20); + + /* Make sure that registers are updated */ + regmap_update_bits(stm32->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG); + + /* Enable interrupt */ + regmap_write(stm32->regmap, TIM_SR, 0); + regmap_update_bits(stm32->regmap, TIM_DIER, TIM_DIER_UIE, TIM_DIER_UIE); + + /* Enable controller */ + regmap_update_bits(stm32->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN); + + return 0; +} + +static int stm32_timer_stop(struct stm32_iio_timer_dev *stm32) +{ + if (!stm32->own_timer) + return 0; + + /* Stop timer */ + regmap_update_bits(stm32->regmap, TIM_DIER, TIM_DIER_UIE, 0); + regmap_update_bits(stm32->regmap, TIM_CR1, TIM_CR1_CEN, 0); + regmap_write(stm32->regmap, TIM_PSC, 0); + regmap_write(stm32->regmap, TIM_ARR, 0); + + clk_disable(stm32->clk); + + stm32->own_timer = false; + stm32->active_trigger = NULL; + + return 0; +} + +static int stm32_set_trigger_state(struct iio_trigger *trig, bool state) +{ + struct stm32_iio_timer_dev *stm32 = iio_trigger_get_drvdata(trig); + + stm32->active_trigger = trig; + + if (state) + return stm32_timer_start(stm32); + else + return stm32_timer_stop(stm32); +} + +static irqreturn_t stm32_timer_irq_handler(int irq, void *private) +{ + struct stm32_iio_timer_dev *stm32 = private; + u32 sr; + + regmap_read(stm32->regmap, TIM_SR, &sr); + regmap_write(stm32->regmap, TIM_SR, 0); + + if ((sr & TIM_SR_UIF) && stm32->active_trigger) + iio_trigger_poll(stm32->active_trigger); + + return IRQ_HANDLED; +} + +static const struct iio_trigger_ops timer_trigger_ops = { + .owner = THIS_MODULE, + .set_trigger_state = stm32_set_trigger_state, +}; + +static int stm32_setup_iio_triggers(struct stm32_iio_timer_dev *stm32) +{ + int i, ret; + const struct stm32_trigger *triggers = stm32->cfg->triggers; + + for (i = 0; i < stm32->cfg->nb_triggers; i++) { + struct iio_trigger *trig; + + trig = devm_iio_trigger_alloc(stm32->dev, + "%s", triggers[i].name); + if (!trig) + return -ENOMEM; + + ret = devm_request_irq(stm32->dev, stm32->irq, + stm32_timer_irq_handler, IRQF_SHARED, + "timer_event", stm32); + if (ret) + return ret; + + trig->dev.parent = stm32->dev->parent; + trig->ops = &timer_trigger_ops; + trig->dev.groups = stm32_trigger_attr_groups; + iio_trigger_set_drvdata(trig, stm32); + + ret = devm_iio_trigger_register(stm32->dev, trig); + if (ret) + return ret; + } + + return 0; +} + +/** + * is_stm32_iio_timer_trigger + * @trig: trigger to be checked + * + * return true if the trigger is a valid stm32 iio timer trigger + * either return false + */ +bool is_stm32_iio_timer_trigger(struct iio_trigger *trig) +{ + return (trig->ops == &timer_trigger_ops); +} +EXPORT_SYMBOL(is_stm32_iio_timer_trigger); + +static int stm32_validate_trigger(struct iio_dev *indio_dev, + struct iio_trigger *trig) +{ + struct stm32_iio_timer_dev *dev = iio_priv(indio_dev); + const struct stm32_valid_trigger *valids = dev->cfg->valids; + int i; + + if (!is_stm32_iio_timer_trigger(trig)) + return -EINVAL; + + for (i = 0; i < dev->cfg->nb_valids; i++) { + if (strcmp(valids[i].name, trig->name) == 0) { + regmap_update_bits(dev->regmap, TIM_SMCR, TIM_SMCR_TS, + valids[i].ts_value << 4); + return 0; + } + } + + return -EINVAL; +} + +static const struct iio_info stm32_trigger_info = { + .driver_module = THIS_MODULE, + .validate_trigger = stm32_validate_trigger, + .attrs = &stm32_timer_attr_group, +}; + +static struct stm32_iio_timer_dev *stm32_setup_iio_device(struct device *dev) +{ + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(struct stm32_iio_timer_dev)); + if (!indio_dev) + return NULL; + + indio_dev->name = dev_name(dev); + indio_dev->dev.parent = dev; + indio_dev->info = &stm32_trigger_info; + indio_dev->modes = INDIO_EVENT_TRIGGERED; + indio_dev->num_channels = 0; + indio_dev->dev.of_node = dev->of_node; + + ret = iio_triggered_event_setup(indio_dev, + NULL, + stm32_timer_irq_handler); + if (ret) + return NULL; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) { + iio_triggered_event_cleanup(indio_dev); + return NULL; + } + + return iio_priv(indio_dev); +} + +static int stm32_iio_timer_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct stm32_iio_timer_dev *stm32; + struct stm32_mfd_timer_dev *mfd = pdev->dev.platform_data; + int ret; + + stm32 = stm32_setup_iio_device(dev); + if (!stm32) + return -ENOMEM; + + stm32->dev = dev; + stm32->regmap = mfd->regmap; + stm32->clk = mfd->clk; + stm32->irq = mfd->irq; + + if (!of_match_node(stm32_trig_of_match, np)->data) + return -EINVAL; + + stm32->cfg = + (struct stm32_trig_cfg *)of_match_node(stm32_trig_of_match, np)->data; + + ret = stm32_setup_iio_triggers(stm32); + if (ret) + return ret; + + platform_set_drvdata(pdev, stm32); + + return 0; +} + +static int stm32_iio_timer_remove(struct platform_device *pdev) +{ + struct stm32_iio_timer_dev *stm32 = platform_get_drvdata(pdev); + + iio_triggered_event_cleanup((struct iio_dev *)stm32); + + return 0; +} + +static struct platform_driver stm32_iio_timer_driver = { + .probe = stm32_iio_timer_probe, + .remove = stm32_iio_timer_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = stm32_trig_of_match, + }, +}; +module_platform_driver(stm32_iio_timer_driver); + +MODULE_ALIAS("platform:" DRIVER_NAME); +MODULE_DESCRIPTION("STMicroelectronics STM32 iio timer driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig index 809b2e7..f2af4fe 100644 --- a/drivers/iio/trigger/Kconfig +++ b/drivers/iio/trigger/Kconfig @@ -46,5 +46,4 @@ config IIO_SYSFS_TRIGGER
To compile this driver as a module, choose M here: the module will be called iio-trig-sysfs. - endmenu diff --git a/include/linux/iio/timer/stm32-iio-timers.h b/include/linux/iio/timer/stm32-iio-timers.h new file mode 100644 index 0000000..c91ddbd --- /dev/null +++ b/include/linux/iio/timer/stm32-iio-timers.h @@ -0,0 +1,25 @@ +/* + * stm32-iio-timers.h + * + * Copyright (C) STMicroelectronics 2016 + * Author: Benjamin Gaignard benjamin.gaignard@st.com for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _STM32_TRIGGERS_H_ +#define _STM32_TRIGGERS_H_ + +#define TIM1_TRGO "tim1_trgo" +#define TIM2_TRGO "tim2_trgo" +#define TIM3_TRGO "tim3_trgo" +#define TIM4_TRGO "tim4_trgo" +#define TIM5_TRGO "tim5_trgo" +#define TIM6_TRGO "tim6_trgo" +#define TIM7_TRGO "tim7_trgo" +#define TIM8_TRGO "tim8_trgo" +#define TIM9_TRGO "tim9_trgo" +#define TIM12_TRGO "tim12_trgo" + +bool is_stm32_iio_timer_trigger(struct iio_trigger *trig); + +#endif
Add timers MFD and childs into DT for stm32f4. Define and enable pwm1 and pwm3 for stm32f469 discovery board
Signed-off-by: Benjamin Gaignard benjamin.gaignard@st.com --- arch/arm/boot/dts/stm32f429.dtsi | 246 ++++++++++++++++++++++++++++++++++ arch/arm/boot/dts/stm32f469-disco.dts | 29 ++++ 2 files changed, 275 insertions(+)
diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi index bca491d..28a0fe9 100644 --- a/arch/arm/boot/dts/stm32f429.dtsi +++ b/arch/arm/boot/dts/stm32f429.dtsi @@ -355,6 +355,21 @@ slew-rate = <2>; }; }; + + pwm1_pins: pwm@1 { + pins { + pinmux = <STM32F429_PA8_FUNC_TIM1_CH1>, + <STM32F429_PB13_FUNC_TIM1_CH1N>, + <STM32F429_PB12_FUNC_TIM1_BKIN>; + }; + }; + + pwm3_pins: pwm@3 { + pins { + pinmux = <STM32F429_PB4_FUNC_TIM3_CH1>, + <STM32F429_PB5_FUNC_TIM3_CH2>; + }; + }; };
rcc: rcc@40023810 { @@ -426,6 +441,237 @@ interrupts = <80>; clocks = <&rcc 0 38>; }; + + mfd_timer1: mfdtimer1@40010000 { + compatible = "st,stm32-mfd-timer1"; + reg = <0x40010000 0x400>; + clocks = <&rcc 0 160>; + clock-names = "mfd_timer_clk"; + interrupts = <27>; + status = "disabled"; + + pwm1: pwm1@40010000 { + compatible = "st,stm32-pwm1"; + status = "disabled"; + }; + + iiotimer1: iiotimer1@40010000 { + compatible = "st,stm32-iio-timer1"; + status = "disabled"; + }; + }; + + mfd_timer2: mfdtimer2@40000000 { + compatible = "st,stm32-mfd-timer2"; + reg = <0x40000000 0x400>; + clocks = <&rcc 0 128>; + clock-names = "mfd_timer_clk"; + interrupts = <28>; + status = "disabled"; + + pwm2: pwm2@40000000 { + compatible = "st,stm32-pwm2"; + status = "disabled"; + }; + iiotimer2: iiotimer2@40000000 { + compatible = "st,stm32-iio-timer2"; + status = "disabled"; + }; + }; + + mfd_timer3: mfdtimer3@40000400 { + compatible = "st,stm32-mfd-timer3"; + reg = <0x40000400 0x400>; + clocks = <&rcc 0 129>; + clock-names = "mfd_timer_clk"; + interrupts = <29>; + status = "disabled"; + + pwm3: pwm3@40000400 { + compatible = "st,stm32-pwm3"; + status = "disabled"; + }; + iiotimer3: iiotimer3@40000400 { + compatible = "st,stm32-iio-timer3"; + status = "disabled"; + }; + }; + + mfd_timer4: mfdtimer4@40000800 { + compatible = "st,stm32-mfd-timer4"; + reg = <0x40000800 0x400>; + clocks = <&rcc 0 130>; + clock-names = "mfd_timer_clk"; + interrupts = <30>; + status = "disabled"; + + pwm4: pwm4@40000800 { + compatible = "st,stm32-pwm4"; + status = "disabled"; + }; + iiotimer4: iiotimer4@40000800 { + compatible = "st,stm32-iio-timer4"; + status = "disabled"; + }; + }; + + mfd_timer5: mfdtimer5@40000C00 { + compatible = "st,stm32-mfd-timer5"; + reg = <0x40000C00 0x400>; + clocks = <&rcc 0 131>; + clock-names = "mfd_timer_clk"; + interrupts = <50>; + status = "disabled"; + + pwm5: pwm5@40000C00 { + compatible = "st,stm32-pwm5"; + status = "disabled"; + }; + iiotimer5: iiotimer5@40000800 { + compatible = "st,stm32-iio-timer5"; + status = "disabled"; + }; + }; + + mfd_timer6: mfdtimer6@40001000 { + compatible = "st,stm32-mfd-timer6"; + reg = <0x40001000 0x400>; + clocks = <&rcc 0 132>; + clock-names = "mfd_timer_clk"; + interrupts = <54>; + status = "disabled"; + + iiotimer6: iiotimer6@40001000 { + compatible = "st,stm32-iio-timer6"; + status = "disabled"; + }; + }; + + mfd_timer7: mfdtimer7@40001400 { + compatible = "st,stm32-mfd-timer7"; + reg = <0x40001400 0x400>; + clocks = <&rcc 0 133>; + clock-names = "mfd_timer_clk"; + interrupts = <55>; + status = "disabled"; + + iiotimer7: iiotimer7@40001400 { + compatible = "st,stm32-iio-timer7"; + status = "disabled"; + }; + }; + + mfd_timer8: mfdtimer8@40010400 { + compatible = "st,stm32-mfd-timer8"; + reg = <0x40010400 0x400>; + clocks = <&rcc 0 161>; + clock-names = "mfd_timer_clk"; + interrupts = <46>; + status = "disabled"; + + pwm8: pwm8@40010400 { + compatible = "st,stm32-pwm8"; + status = "disabled"; + }; + + iiotimer8: iiotimer7@40010400 { + compatible = "st,stm32-iio-timer8"; + status = "disabled"; + }; + }; + + mfd_timer9: mfdtimer9@40014000 { + compatible = "st,stm32-mfd-timer9"; + reg = <0x40014000 0x400>; + clocks = <&rcc 0 176>; + clock-names = "mfd_timer_clk"; + interrupts = <24>; + status = "disabled"; + + pwm9: pwm9@40014000 { + compatible = "st,stm32-pwm9"; + status = "disabled"; + }; + + iiotimer9: iiotimer9@40014000 { + compatible = "st,stm32-iio-timer9"; + status = "disabled"; + }; + }; + + mfd_timer10: mfdtimer10@40014400 { + compatible = "st,stm32-mfd-timer10"; + reg = <0x40014400 0x400>; + clocks = <&rcc 0 177>; + clock-names = "mfd_timer_clk"; + interrupts = <25>; + status = "disabled"; + + pwm10: pwm10@40014400 { + compatible = "st,stm32-pwm10"; + status = "disabled"; + }; + }; + + mfd_timer11: mfdtimer11@40014800 { + compatible = "st,stm32-mfd-timer11"; + reg = <0x40014800 0x400>; + clocks = <&rcc 0 178>; + clock-names = "mfd_timer_clk"; + interrupts = <26>; + status = "disabled"; + + pwm11: pwm11@40014800 { + compatible = "st,stm32-pwm11"; + status = "disabled"; + }; + }; + + mfd_timer12: mfdtimer12@40001800 { + compatible = "st,stm32-mfd-timer12"; + reg = <0x40001800 0x400>; + clocks = <&rcc 0 134>; + clock-names = "mfd_timer_clk"; + interrupts = <43>; + status = "disabled"; + + pwm12: pwm12@40001800 { + compatible = "st,stm32-pwm12"; + status = "disabled"; + }; + iiotimer12: iiotimer12@40001800 { + compatible = "st,stm32-iio-timer12"; + status = "disabled"; + }; + }; + + mfd_timer13: mfdtimer13@40001C00 { + compatible = "st,stm32-mfd-timer13"; + reg = <0x40001C00 0x400>; + clocks = <&rcc 0 135>; + clock-names = "mfd_timer_clk"; + interrupts = <44>; + status = "disabled"; + + pwm13: pwm13@40001C00 { + compatible = "st,stm32-pwm13"; + status = "disabled"; + }; + }; + + mfd_timer14: mfdtimer14@40002000 { + compatible = "st,stm32-mfd-timer14"; + reg = <0x40002000 0x400>; + clocks = <&rcc 0 136>; + clock-names = "mfd_timer_clk"; + interrupts = <45>; + status = "disabled"; + + pwm14: pwm14@40002000 { + compatible = "st,stm32-pwm14"; + status = "disabled"; + }; + }; }; };
diff --git a/arch/arm/boot/dts/stm32f469-disco.dts b/arch/arm/boot/dts/stm32f469-disco.dts index 8a163d7..a8f1788 100644 --- a/arch/arm/boot/dts/stm32f469-disco.dts +++ b/arch/arm/boot/dts/stm32f469-disco.dts @@ -81,3 +81,32 @@ &usart3 { status = "okay"; }; + +&mfd_timer1 { + status = "okay"; +}; + +&pwm1 { + pinctrl-0 = <&pwm1_pins>; + pinctrl-names = "default"; + st,breakinput-polarity = <0>; + status = "okay"; +}; + +&iiotimer1 { + status = "okay"; +}; + +&mfd_timer3 { + status = "okay"; +}; + +&pwm3 { + pinctrl-0 = <&pwm3_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&iiotimer3 { + status = "okay"; +};
Hi Benjamin,
On 11/22/2016 05:13 PM, Benjamin Gaignard wrote:
Add timers MFD and childs into DT for stm32f4. Define and enable pwm1 and pwm3 for stm32f469 discovery board
Signed-off-by: Benjamin Gaignard benjamin.gaignard@st.com
If you have to send a v2 for this series please change commit header by: "ARM: dts: stm32: ..." (if not I will do it by myself)
arch/arm/boot/dts/stm32f429.dtsi | 246 ++++++++++++++++++++++++++++++++++ arch/arm/boot/dts/stm32f469-disco.dts | 29 ++++ 2 files changed, 275 insertions(+)
diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi index bca491d..28a0fe9 100644 --- a/arch/arm/boot/dts/stm32f429.dtsi +++ b/arch/arm/boot/dts/stm32f429.dtsi @@ -355,6 +355,21 @@ slew-rate = <2>; }; };
pwm1_pins: pwm@1 {
pins {
pinmux = <STM32F429_PA8_FUNC_TIM1_CH1>,
<STM32F429_PB13_FUNC_TIM1_CH1N>,
<STM32F429_PB12_FUNC_TIM1_BKIN>;
};
};
pwm3_pins: pwm@3 {
pins {
pinmux = <STM32F429_PB4_FUNC_TIM3_CH1>,
<STM32F429_PB5_FUNC_TIM3_CH2>;
};
};
};
rcc: rcc@40023810 {
@@ -426,6 +441,237 @@ interrupts = <80>; clocks = <&rcc 0 38>; };
mfd_timer1: mfdtimer1@40010000 {
compatible = "st,stm32-mfd-timer1";
reg = <0x40010000 0x400>;
clocks = <&rcc 0 160>;
clock-names = "mfd_timer_clk";
interrupts = <27>;
status = "disabled";
pwm1: pwm1@40010000 {
compatible = "st,stm32-pwm1";
status = "disabled";
};
iiotimer1: iiotimer1@40010000 {
compatible = "st,stm32-iio-timer1";
status = "disabled";
};
};
mfd_timer2: mfdtimer2@40000000 {
compatible = "st,stm32-mfd-timer2";
reg = <0x40000000 0x400>;
clocks = <&rcc 0 128>;
clock-names = "mfd_timer_clk";
interrupts = <28>;
status = "disabled";
pwm2: pwm2@40000000 {
compatible = "st,stm32-pwm2";
status = "disabled";
};
iiotimer2: iiotimer2@40000000 {
compatible = "st,stm32-iio-timer2";
status = "disabled";
};
};
mfd_timer3: mfdtimer3@40000400 {
compatible = "st,stm32-mfd-timer3";
reg = <0x40000400 0x400>;
clocks = <&rcc 0 129>;
clock-names = "mfd_timer_clk";
interrupts = <29>;
status = "disabled";
pwm3: pwm3@40000400 {
compatible = "st,stm32-pwm3";
status = "disabled";
};
iiotimer3: iiotimer3@40000400 {
compatible = "st,stm32-iio-timer3";
status = "disabled";
};
};
mfd_timer4: mfdtimer4@40000800 {
compatible = "st,stm32-mfd-timer4";
reg = <0x40000800 0x400>;
clocks = <&rcc 0 130>;
clock-names = "mfd_timer_clk";
interrupts = <30>;
status = "disabled";
pwm4: pwm4@40000800 {
compatible = "st,stm32-pwm4";
status = "disabled";
};
iiotimer4: iiotimer4@40000800 {
compatible = "st,stm32-iio-timer4";
status = "disabled";
};
};
mfd_timer5: mfdtimer5@40000C00 {
compatible = "st,stm32-mfd-timer5";
reg = <0x40000C00 0x400>;
clocks = <&rcc 0 131>;
clock-names = "mfd_timer_clk";
interrupts = <50>;
status = "disabled";
pwm5: pwm5@40000C00 {
compatible = "st,stm32-pwm5";
status = "disabled";
};
iiotimer5: iiotimer5@40000800 {
compatible = "st,stm32-iio-timer5";
status = "disabled";
};
};
mfd_timer6: mfdtimer6@40001000 {
compatible = "st,stm32-mfd-timer6";
reg = <0x40001000 0x400>;
clocks = <&rcc 0 132>;
clock-names = "mfd_timer_clk";
interrupts = <54>;
status = "disabled";
iiotimer6: iiotimer6@40001000 {
compatible = "st,stm32-iio-timer6";
status = "disabled";
};
};
mfd_timer7: mfdtimer7@40001400 {
compatible = "st,stm32-mfd-timer7";
reg = <0x40001400 0x400>;
clocks = <&rcc 0 133>;
clock-names = "mfd_timer_clk";
interrupts = <55>;
status = "disabled";
iiotimer7: iiotimer7@40001400 {
compatible = "st,stm32-iio-timer7";
status = "disabled";
};
};
mfd_timer8: mfdtimer8@40010400 {
compatible = "st,stm32-mfd-timer8";
reg = <0x40010400 0x400>;
clocks = <&rcc 0 161>;
clock-names = "mfd_timer_clk";
interrupts = <46>;
status = "disabled";
pwm8: pwm8@40010400 {
compatible = "st,stm32-pwm8";
status = "disabled";
};
iiotimer8: iiotimer7@40010400 {
compatible = "st,stm32-iio-timer8";
status = "disabled";
};
};
mfd_timer9: mfdtimer9@40014000 {
compatible = "st,stm32-mfd-timer9";
reg = <0x40014000 0x400>;
clocks = <&rcc 0 176>;
clock-names = "mfd_timer_clk";
interrupts = <24>;
status = "disabled";
pwm9: pwm9@40014000 {
compatible = "st,stm32-pwm9";
status = "disabled";
};
iiotimer9: iiotimer9@40014000 {
compatible = "st,stm32-iio-timer9";
status = "disabled";
};
};
mfd_timer10: mfdtimer10@40014400 {
compatible = "st,stm32-mfd-timer10";
reg = <0x40014400 0x400>;
clocks = <&rcc 0 177>;
clock-names = "mfd_timer_clk";
interrupts = <25>;
status = "disabled";
pwm10: pwm10@40014400 {
compatible = "st,stm32-pwm10";
status = "disabled";
};
};
mfd_timer11: mfdtimer11@40014800 {
compatible = "st,stm32-mfd-timer11";
reg = <0x40014800 0x400>;
clocks = <&rcc 0 178>;
clock-names = "mfd_timer_clk";
interrupts = <26>;
status = "disabled";
pwm11: pwm11@40014800 {
compatible = "st,stm32-pwm11";
status = "disabled";
};
};
mfd_timer12: mfdtimer12@40001800 {
compatible = "st,stm32-mfd-timer12";
reg = <0x40001800 0x400>;
clocks = <&rcc 0 134>;
clock-names = "mfd_timer_clk";
interrupts = <43>;
status = "disabled";
pwm12: pwm12@40001800 {
compatible = "st,stm32-pwm12";
status = "disabled";
};
iiotimer12: iiotimer12@40001800 {
compatible = "st,stm32-iio-timer12";
status = "disabled";
};
};
mfd_timer13: mfdtimer13@40001C00 {
compatible = "st,stm32-mfd-timer13";
reg = <0x40001C00 0x400>;
clocks = <&rcc 0 135>;
clock-names = "mfd_timer_clk";
interrupts = <44>;
status = "disabled";
pwm13: pwm13@40001C00 {
compatible = "st,stm32-pwm13";
status = "disabled";
};
};
mfd_timer14: mfdtimer14@40002000 {
compatible = "st,stm32-mfd-timer14";
reg = <0x40002000 0x400>;
clocks = <&rcc 0 136>;
clock-names = "mfd_timer_clk";
interrupts = <45>;
status = "disabled";
pwm14: pwm14@40002000 {
compatible = "st,stm32-pwm14";
status = "disabled";
};
};};
};
diff --git a/arch/arm/boot/dts/stm32f469-disco.dts b/arch/arm/boot/dts/stm32f469-disco.dts index 8a163d7..a8f1788 100644 --- a/arch/arm/boot/dts/stm32f469-disco.dts +++ b/arch/arm/boot/dts/stm32f469-disco.dts @@ -81,3 +81,32 @@ &usart3 { status = "okay"; };
+&mfd_timer1 {
- status = "okay";
+};
+&pwm1 {
- pinctrl-0 = <&pwm1_pins>;
- pinctrl-names = "default";
- st,breakinput-polarity = <0>;
- status = "okay";
+};
+&iiotimer1 {
- status = "okay";
+};
+&mfd_timer3 {
- status = "okay";
+};
+&pwm3 {
- pinctrl-0 = <&pwm3_pins>;
- pinctrl-names = "default";
- status = "okay";
+};
+&iiotimer3 {
- status = "okay";
+};
On Tue, 22 Nov 2016, Benjamin Gaignard wrote:
Add timers MFD and childs into DT for stm32f4. Define and enable pwm1 and pwm3 for stm32f469 discovery board
Signed-off-by: Benjamin Gaignard benjamin.gaignard@st.com
arch/arm/boot/dts/stm32f429.dtsi | 246 ++++++++++++++++++++++++++++++++++ arch/arm/boot/dts/stm32f469-disco.dts | 29 ++++ 2 files changed, 275 insertions(+)
diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi index bca491d..28a0fe9 100644 --- a/arch/arm/boot/dts/stm32f429.dtsi +++ b/arch/arm/boot/dts/stm32f429.dtsi @@ -355,6 +355,21 @@ slew-rate = <2>; }; };
pwm1_pins: pwm@1 {
pins {
pinmux = <STM32F429_PA8_FUNC_TIM1_CH1>,
<STM32F429_PB13_FUNC_TIM1_CH1N>,
<STM32F429_PB12_FUNC_TIM1_BKIN>;
};
};
pwm3_pins: pwm@3 {
pins {
pinmux = <STM32F429_PB4_FUNC_TIM3_CH1>,
<STM32F429_PB5_FUNC_TIM3_CH2>;
};
};};
rcc: rcc@40023810 { @@ -426,6 +441,237 @@ interrupts = <80>; clocks = <&rcc 0 38>; };
mfd_timer1: mfdtimer1@40010000 {
Do you reference this node?
If not, it should read:
advanced-control@40010000
compatible = "st,stm32-mfd-timer1";
"st,stm32-advanced-control"
reg = <0x40010000 0x400>;
clocks = <&rcc 0 160>;
clock-names = "mfd_timer_clk";
"clk_int"
interrupts = <27>;
This is a timer property.
Also move the associated registration C code into the timer driver.
status = "disabled";
pwm1: pwm1@40010000 {
pwm@0 {
compatible = "st,stm32-pwm1";
st,stm32-advanced-control-pwm
status = "disabled";
};
iiotimer1: iiotimer1@40010000 {
Same here:
timer@0
compatible = "st,stm32-iio-timer1";
st,stm32-advanced-control-timer
status = "disabled";
};
};
mfd_timer2: mfdtimer2@40000000 {
compatible = "st,stm32-mfd-timer2";
reg = <0x40000000 0x400>;
clocks = <&rcc 0 128>;
clock-names = "mfd_timer_clk";
interrupts = <28>;
status = "disabled";
pwm2: pwm2@40000000 {
compatible = "st,stm32-pwm2";
status = "disabled";
};
iiotimer2: iiotimer2@40000000 {
compatible = "st,stm32-iio-timer2";
status = "disabled";
};
};
mfd_timer3: mfdtimer3@40000400 {
compatible = "st,stm32-mfd-timer3";
reg = <0x40000400 0x400>;
clocks = <&rcc 0 129>;
clock-names = "mfd_timer_clk";
interrupts = <29>;
status = "disabled";
pwm3: pwm3@40000400 {
compatible = "st,stm32-pwm3";
status = "disabled";
};
iiotimer3: iiotimer3@40000400 {
compatible = "st,stm32-iio-timer3";
status = "disabled";
};
};
mfd_timer4: mfdtimer4@40000800 {
compatible = "st,stm32-mfd-timer4";
reg = <0x40000800 0x400>;
clocks = <&rcc 0 130>;
clock-names = "mfd_timer_clk";
interrupts = <30>;
status = "disabled";
pwm4: pwm4@40000800 {
compatible = "st,stm32-pwm4";
status = "disabled";
};
iiotimer4: iiotimer4@40000800 {
compatible = "st,stm32-iio-timer4";
status = "disabled";
};
};
mfd_timer5: mfdtimer5@40000C00 {
compatible = "st,stm32-mfd-timer5";
reg = <0x40000C00 0x400>;
clocks = <&rcc 0 131>;
clock-names = "mfd_timer_clk";
interrupts = <50>;
status = "disabled";
pwm5: pwm5@40000C00 {
compatible = "st,stm32-pwm5";
status = "disabled";
};
iiotimer5: iiotimer5@40000800 {
compatible = "st,stm32-iio-timer5";
status = "disabled";
};
};
mfd_timer6: mfdtimer6@40001000 {
compatible = "st,stm32-mfd-timer6";
reg = <0x40001000 0x400>;
clocks = <&rcc 0 132>;
clock-names = "mfd_timer_clk";
interrupts = <54>;
status = "disabled";
iiotimer6: iiotimer6@40001000 {
compatible = "st,stm32-iio-timer6";
status = "disabled";
};
};
mfd_timer7: mfdtimer7@40001400 {
compatible = "st,stm32-mfd-timer7";
reg = <0x40001400 0x400>;
clocks = <&rcc 0 133>;
clock-names = "mfd_timer_clk";
interrupts = <55>;
status = "disabled";
iiotimer7: iiotimer7@40001400 {
compatible = "st,stm32-iio-timer7";
status = "disabled";
};
};
mfd_timer8: mfdtimer8@40010400 {
compatible = "st,stm32-mfd-timer8";
reg = <0x40010400 0x400>;
clocks = <&rcc 0 161>;
clock-names = "mfd_timer_clk";
interrupts = <46>;
status = "disabled";
pwm8: pwm8@40010400 {
compatible = "st,stm32-pwm8";
status = "disabled";
};
iiotimer8: iiotimer7@40010400 {
compatible = "st,stm32-iio-timer8";
status = "disabled";
};
};
mfd_timer9: mfdtimer9@40014000 {
compatible = "st,stm32-mfd-timer9";
reg = <0x40014000 0x400>;
clocks = <&rcc 0 176>;
clock-names = "mfd_timer_clk";
interrupts = <24>;
status = "disabled";
pwm9: pwm9@40014000 {
compatible = "st,stm32-pwm9";
status = "disabled";
};
iiotimer9: iiotimer9@40014000 {
compatible = "st,stm32-iio-timer9";
status = "disabled";
};
};
mfd_timer10: mfdtimer10@40014400 {
compatible = "st,stm32-mfd-timer10";
reg = <0x40014400 0x400>;
clocks = <&rcc 0 177>;
clock-names = "mfd_timer_clk";
interrupts = <25>;
status = "disabled";
pwm10: pwm10@40014400 {
compatible = "st,stm32-pwm10";
status = "disabled";
};
};
mfd_timer11: mfdtimer11@40014800 {
compatible = "st,stm32-mfd-timer11";
reg = <0x40014800 0x400>;
clocks = <&rcc 0 178>;
clock-names = "mfd_timer_clk";
interrupts = <26>;
status = "disabled";
pwm11: pwm11@40014800 {
compatible = "st,stm32-pwm11";
status = "disabled";
};
};
mfd_timer12: mfdtimer12@40001800 {
compatible = "st,stm32-mfd-timer12";
reg = <0x40001800 0x400>;
clocks = <&rcc 0 134>;
clock-names = "mfd_timer_clk";
interrupts = <43>;
status = "disabled";
pwm12: pwm12@40001800 {
compatible = "st,stm32-pwm12";
status = "disabled";
};
iiotimer12: iiotimer12@40001800 {
compatible = "st,stm32-iio-timer12";
status = "disabled";
};
};
mfd_timer13: mfdtimer13@40001C00 {
compatible = "st,stm32-mfd-timer13";
reg = <0x40001C00 0x400>;
clocks = <&rcc 0 135>;
clock-names = "mfd_timer_clk";
interrupts = <44>;
status = "disabled";
pwm13: pwm13@40001C00 {
compatible = "st,stm32-pwm13";
status = "disabled";
};
};
mfd_timer14: mfdtimer14@40002000 {
compatible = "st,stm32-mfd-timer14";
reg = <0x40002000 0x400>;
clocks = <&rcc 0 136>;
clock-names = "mfd_timer_clk";
interrupts = <45>;
status = "disabled";
pwm14: pwm14@40002000 {
compatible = "st,stm32-pwm14";
status = "disabled";
};
};};
}; diff --git a/arch/arm/boot/dts/stm32f469-disco.dts b/arch/arm/boot/dts/stm32f469-disco.dts index 8a163d7..a8f1788 100644 --- a/arch/arm/boot/dts/stm32f469-disco.dts +++ b/arch/arm/boot/dts/stm32f469-disco.dts @@ -81,3 +81,32 @@ &usart3 { status = "okay"; };
+&mfd_timer1 {
- status = "okay";
+};
+&pwm1 {
- pinctrl-0 = <&pwm1_pins>;
- pinctrl-names = "default";
- st,breakinput-polarity = <0>;
Is this documented?
I'm sure we have generic polarity properties somewhere already?
- status = "okay";
+};
+&iiotimer1 {
- status = "okay";
+};
+&mfd_timer3 {
- status = "okay";
+};
+&pwm3 {
- pinctrl-0 = <&pwm3_pins>;
- pinctrl-names = "default";
- status = "okay";
+};
+&iiotimer3 {
- status = "okay";
+};
I've always disliked this way of referencing nodes!
Any chance we can represent them in a hierarchy, so we don't lose that information and we can get rid of all those horrible labels?
I'm happy to do the work.
linaro-kernel@lists.linaro.org