* [PATCH v19 1/3] dt-bindings: pwm: opencores: Update compatibles, examples and maintainers
2026-06-15 15:57 [PATCH v19 0/3] Add OpenCores PTC PWM support Hal Feng
@ 2026-06-15 15:57 ` Hal Feng
2026-06-15 16:21 ` Conor Dooley
2026-06-15 16:24 ` sashiko-bot
2026-06-15 15:57 ` [PATCH v19 2/3] riscv: dts: starfive: Correct pwm nodes Hal Feng
2026-06-15 15:57 ` [PATCH v19 3/3] pwm: Add OpenCores PTC PWM driver Hal Feng
2 siblings, 2 replies; 7+ messages in thread
From: Hal Feng @ 2026-06-15 15:57 UTC (permalink / raw)
To: Uwe Kleine-König, Philipp Zabel, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Emil Renner Berthing,
Palmer Dabbelt, Paul Walmsley, Albert Ou
Cc: Hal Feng, linux-pwm, devicetree, linux-riscv, linux-kernel
Remove the jh8100 compatible since the JH8100 SoC has been canceled and
will not be released. Add the jhb100 compatible to replace it.
Use a oneOf construct to support the single-string opencores,pwm-v1
compatible.
Change the register size in examples to 0x10, since an OpenCores PTC IP
has only 4 32-bit registers: CNTR, HRC, LRC and CTRL.
I will maintain this pwm module in place of William.
Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
---
.../devicetree/bindings/pwm/opencores,pwm.yaml | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/Documentation/devicetree/bindings/pwm/opencores,pwm.yaml b/Documentation/devicetree/bindings/pwm/opencores,pwm.yaml
index 52a59d245cdb..5f05606a2d3d 100644
--- a/Documentation/devicetree/bindings/pwm/opencores,pwm.yaml
+++ b/Documentation/devicetree/bindings/pwm/opencores,pwm.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: OpenCores PWM controller
maintainers:
- - William Qiu <william.qiu@starfivetech.com>
+ - Hal Feng <hal.feng@starfivetech.com>
description:
The OpenCores PTC ip core contains a PWM controller. When operating in PWM
@@ -19,12 +19,14 @@ allOf:
properties:
compatible:
- items:
- - enum:
- - starfive,jh7100-pwm
- - starfive,jh7110-pwm
- - starfive,jh8100-pwm
+ oneOf:
- const: opencores,pwm-v1
+ - items:
+ - enum:
+ - starfive,jh7100-pwm
+ - starfive,jh7110-pwm
+ - starfive,jhb100-pwm
+ - const: opencores,pwm-v1
reg:
maxItems: 1
@@ -49,7 +51,7 @@ examples:
- |
pwm@12490000 {
compatible = "starfive,jh7110-pwm", "opencores,pwm-v1";
- reg = <0x12490000 0x10000>;
+ reg = <0x12490000 0x10>;
clocks = <&clkgen 181>;
resets = <&rstgen 109>;
#pwm-cells = <3>;
--
2.43.2
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH v19 1/3] dt-bindings: pwm: opencores: Update compatibles, examples and maintainers
2026-06-15 15:57 ` [PATCH v19 1/3] dt-bindings: pwm: opencores: Update compatibles, examples and maintainers Hal Feng
@ 2026-06-15 16:21 ` Conor Dooley
2026-06-15 16:24 ` sashiko-bot
1 sibling, 0 replies; 7+ messages in thread
From: Conor Dooley @ 2026-06-15 16:21 UTC (permalink / raw)
To: Hal Feng
Cc: Uwe Kleine-König, Philipp Zabel, Rob Herring,
Krzysztof Kozlowski, Emil Renner Berthing, Palmer Dabbelt,
Paul Walmsley, Albert Ou, linux-pwm, devicetree, linux-riscv,
linux-kernel
[-- Attachment #1: Type: text/plain, Size: 2289 bytes --]
On Mon, Jun 15, 2026 at 11:57:57PM +0800, Hal Feng wrote:
> Remove the jh8100 compatible since the JH8100 SoC has been canceled and
> will not be released. Add the jhb100 compatible to replace it.
> Use a oneOf construct to support the single-string opencores,pwm-v1
> compatible.
No thanks. Simple as this IP might be, I still want soc-specific
compatibles to be a requirement.
pw-bot: changes-requested
Thanks,
Conor.
>
> Change the register size in examples to 0x10, since an OpenCores PTC IP
> has only 4 32-bit registers: CNTR, HRC, LRC and CTRL.
>
> I will maintain this pwm module in place of William.
>
> Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
> ---
> .../devicetree/bindings/pwm/opencores,pwm.yaml | 16 +++++++++-------
> 1 file changed, 9 insertions(+), 7 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/pwm/opencores,pwm.yaml b/Documentation/devicetree/bindings/pwm/opencores,pwm.yaml
> index 52a59d245cdb..5f05606a2d3d 100644
> --- a/Documentation/devicetree/bindings/pwm/opencores,pwm.yaml
> +++ b/Documentation/devicetree/bindings/pwm/opencores,pwm.yaml
> @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
> title: OpenCores PWM controller
>
> maintainers:
> - - William Qiu <william.qiu@starfivetech.com>
> + - Hal Feng <hal.feng@starfivetech.com>
>
> description:
> The OpenCores PTC ip core contains a PWM controller. When operating in PWM
> @@ -19,12 +19,14 @@ allOf:
>
> properties:
> compatible:
> - items:
> - - enum:
> - - starfive,jh7100-pwm
> - - starfive,jh7110-pwm
> - - starfive,jh8100-pwm
> + oneOf:
> - const: opencores,pwm-v1
> + - items:
> + - enum:
> + - starfive,jh7100-pwm
> + - starfive,jh7110-pwm
> + - starfive,jhb100-pwm
> + - const: opencores,pwm-v1
>
> reg:
> maxItems: 1
> @@ -49,7 +51,7 @@ examples:
> - |
> pwm@12490000 {
> compatible = "starfive,jh7110-pwm", "opencores,pwm-v1";
> - reg = <0x12490000 0x10000>;
> + reg = <0x12490000 0x10>;
> clocks = <&clkgen 181>;
> resets = <&rstgen 109>;
> #pwm-cells = <3>;
> --
> 2.43.2
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH v19 1/3] dt-bindings: pwm: opencores: Update compatibles, examples and maintainers
2026-06-15 15:57 ` [PATCH v19 1/3] dt-bindings: pwm: opencores: Update compatibles, examples and maintainers Hal Feng
2026-06-15 16:21 ` Conor Dooley
@ 2026-06-15 16:24 ` sashiko-bot
1 sibling, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-06-15 16:24 UTC (permalink / raw)
To: Hal Feng; +Cc: robh, conor+dt, devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Invalid YAML indentation under the `items` block violates the subsystem's yamllint rules, causing `make dt_binding_check` to fail.
--
commit f6d46c997f1ef83c6c55a27e0e3260fdf27add18
Author: Hal Feng <hal.feng@starfivetech.com>
dt-bindings: pwm: opencores: Update compatibles, examples and maintainers
Remove the jh8100 compatible since the JH8100 SoC has been canceled and
will not be released. Add the jhb100 compatible to replace it. Use a oneOf
construct to support the single-string opencores,pwm-v1 compatible.
> diff --git a/Documentation/devicetree/bindings/pwm/opencores,pwm.yaml b/Documentation/devicetree/bindings/pwm/opencores,pwm.yaml
> index 52a59d245cdb1..5f05606a2d3d4 100644
> --- a/Documentation/devicetree/bindings/pwm/opencores,pwm.yaml
> +++ b/Documentation/devicetree/bindings/pwm/opencores,pwm.yaml
[ ... ]
> @@ -19,12 +19,14 @@ allOf:
>
> properties:
> compatible:
> - items:
> - - enum:
> - - starfive,jh7100-pwm
> - - starfive,jh7110-pwm
> - - starfive,jh8100-pwm
> + oneOf:
> - const: opencores,pwm-v1
> + - items:
> + - enum:
> + - starfive,jh7100-pwm
> + - starfive,jh7110-pwm
> + - starfive,jhb100-pwm
> + - const: opencores,pwm-v1
[Severity: Low]
Does this formatting cause make dt_binding_check to fail? It looks like the
YAML sequence markers under the items block are indented to 8 spaces,
matching the parent mapping key, instead of being indented to 10 spaces.
This might violate the yamllint indent-sequences rules.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260615155759.129210-1-hal.feng@starfivetech.com?part=1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v19 2/3] riscv: dts: starfive: Correct pwm nodes
2026-06-15 15:57 [PATCH v19 0/3] Add OpenCores PTC PWM support Hal Feng
2026-06-15 15:57 ` [PATCH v19 1/3] dt-bindings: pwm: opencores: Update compatibles, examples and maintainers Hal Feng
@ 2026-06-15 15:57 ` Hal Feng
2026-06-15 15:57 ` [PATCH v19 3/3] pwm: Add OpenCores PTC PWM driver Hal Feng
2 siblings, 0 replies; 7+ messages in thread
From: Hal Feng @ 2026-06-15 15:57 UTC (permalink / raw)
To: Uwe Kleine-König, Philipp Zabel, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Emil Renner Berthing,
Palmer Dabbelt, Paul Walmsley, Albert Ou
Cc: Hal Feng, linux-pwm, devicetree, linux-riscv, linux-kernel
Each of the StarFive JH7100/JH7110 SoCs has 8 OpenCores PTC IP
cores. One OpenCores PTC IP core can output one PWM channel.
Change the register size to 0x10, since an OpenCores PTC IP
has only 4 32-bit registers: CNTR, HRC, LRC and CTRL.
Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
---
.../boot/dts/starfive/jh7100-common.dtsi | 28 ++++++--
arch/riscv/boot/dts/starfive/jh7100.dtsi | 67 ++++++++++++++++++-
.../boot/dts/starfive/jh7110-common.dtsi | 27 ++++++--
.../boot/dts/starfive/jh7110-milkv-mars.dts | 6 +-
.../dts/starfive/jh7110-milkv-marscm.dtsi | 6 +-
.../dts/starfive/jh7110-pine64-star64.dts | 6 +-
.../jh7110-starfive-visionfive-2-lite.dtsi | 6 +-
.../jh7110-starfive-visionfive-2.dtsi | 6 +-
arch/riscv/boot/dts/starfive/jh7110.dtsi | 67 ++++++++++++++++++-
9 files changed, 198 insertions(+), 21 deletions(-)
diff --git a/arch/riscv/boot/dts/starfive/jh7100-common.dtsi b/arch/riscv/boot/dts/starfive/jh7100-common.dtsi
index ae1a6aeb0aea..85106545090e 100644
--- a/arch/riscv/boot/dts/starfive/jh7100-common.dtsi
+++ b/arch/riscv/boot/dts/starfive/jh7100-common.dtsi
@@ -199,13 +199,23 @@ GPO_I2C2_PAD_SDA_OEN,
};
};
- pwm_pins: pwm-0 {
- pwm-pins {
+ pwm0_pins: pwm0-0 {
+ pwm0-pins {
pinmux = <GPIOMUX(7,
GPO_PWM_PAD_OUT_BIT0,
GPO_PWM_PAD_OE_N_BIT0,
- GPI_NONE)>,
- <GPIOMUX(5,
+ GPI_NONE)>;
+ bias-disable;
+ drive-strength = <35>;
+ input-disable;
+ input-schmitt-disable;
+ slew-rate = <0>;
+ };
+ };
+
+ pwm1_pins: pwm1-0 {
+ pwm1-pins {
+ pinmux = <GPIOMUX(5,
GPO_PWM_PAD_OUT_BIT1,
GPO_PWM_PAD_OE_N_BIT1,
GPI_NONE)>;
@@ -359,9 +369,15 @@ &osc_aud {
clock-frequency = <27000000>;
};
-&pwm {
+&pwm0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pwm0_pins>;
+ status = "okay";
+};
+
+&pwm1 {
pinctrl-names = "default";
- pinctrl-0 = <&pwm_pins>;
+ pinctrl-0 = <&pwm1_pins>;
status = "okay";
};
diff --git a/arch/riscv/boot/dts/starfive/jh7100.dtsi b/arch/riscv/boot/dts/starfive/jh7100.dtsi
index 7de0732b8eab..90438df1f74d 100644
--- a/arch/riscv/boot/dts/starfive/jh7100.dtsi
+++ b/arch/riscv/boot/dts/starfive/jh7100.dtsi
@@ -360,9 +360,72 @@ watchdog@12480000 {
<&rstgen JH7100_RSTN_WDT>;
};
- pwm: pwm@12490000 {
+ pwm0: pwm@12490000 {
compatible = "starfive,jh7100-pwm", "opencores,pwm-v1";
- reg = <0x0 0x12490000 0x0 0x10000>;
+ reg = <0x0 0x12490000 0x0 0x10>;
+ clocks = <&clkgen JH7100_CLK_PWM_APB>;
+ resets = <&rstgen JH7100_RSTN_PWM_APB>;
+ #pwm-cells = <3>;
+ status = "disabled";
+ };
+
+ pwm1: pwm@12490010 {
+ compatible = "starfive,jh7100-pwm", "opencores,pwm-v1";
+ reg = <0x0 0x12490010 0x0 0x10>;
+ clocks = <&clkgen JH7100_CLK_PWM_APB>;
+ resets = <&rstgen JH7100_RSTN_PWM_APB>;
+ #pwm-cells = <3>;
+ status = "disabled";
+ };
+
+ pwm2: pwm@12490020 {
+ compatible = "starfive,jh7100-pwm", "opencores,pwm-v1";
+ reg = <0x0 0x12490020 0x0 0x10>;
+ clocks = <&clkgen JH7100_CLK_PWM_APB>;
+ resets = <&rstgen JH7100_RSTN_PWM_APB>;
+ #pwm-cells = <3>;
+ status = "disabled";
+ };
+
+ pwm3: pwm@12490030 {
+ compatible = "starfive,jh7100-pwm", "opencores,pwm-v1";
+ reg = <0x0 0x12490030 0x0 0x10>;
+ clocks = <&clkgen JH7100_CLK_PWM_APB>;
+ resets = <&rstgen JH7100_RSTN_PWM_APB>;
+ #pwm-cells = <3>;
+ status = "disabled";
+ };
+
+ pwm4: pwm@12498000 {
+ compatible = "starfive,jh7100-pwm", "opencores,pwm-v1";
+ reg = <0x0 0x12498000 0x0 0x10>;
+ clocks = <&clkgen JH7100_CLK_PWM_APB>;
+ resets = <&rstgen JH7100_RSTN_PWM_APB>;
+ #pwm-cells = <3>;
+ status = "disabled";
+ };
+
+ pwm5: pwm@12498010 {
+ compatible = "starfive,jh7100-pwm", "opencores,pwm-v1";
+ reg = <0x0 0x12498010 0x0 0x10>;
+ clocks = <&clkgen JH7100_CLK_PWM_APB>;
+ resets = <&rstgen JH7100_RSTN_PWM_APB>;
+ #pwm-cells = <3>;
+ status = "disabled";
+ };
+
+ pwm6: pwm@12498020 {
+ compatible = "starfive,jh7100-pwm", "opencores,pwm-v1";
+ reg = <0x0 0x12498020 0x0 0x10>;
+ clocks = <&clkgen JH7100_CLK_PWM_APB>;
+ resets = <&rstgen JH7100_RSTN_PWM_APB>;
+ #pwm-cells = <3>;
+ status = "disabled";
+ };
+
+ pwm7: pwm@12498030 {
+ compatible = "starfive,jh7100-pwm", "opencores,pwm-v1";
+ reg = <0x0 0x12498030 0x0 0x10>;
clocks = <&clkgen JH7100_CLK_PWM_APB>;
resets = <&rstgen JH7100_RSTN_PWM_APB>;
#pwm-cells = <3>;
diff --git a/arch/riscv/boot/dts/starfive/jh7110-common.dtsi b/arch/riscv/boot/dts/starfive/jh7110-common.dtsi
index a7a1c09a2c90..64de468f2c31 100644
--- a/arch/riscv/boot/dts/starfive/jh7110-common.dtsi
+++ b/arch/riscv/boot/dts/starfive/jh7110-common.dtsi
@@ -326,9 +326,14 @@ uboot@100000 {
};
};
-&pwm {
+&pwm0 {
pinctrl-names = "default";
- pinctrl-0 = <&pwm_pins>;
+ pinctrl-0 = <&pwm0_pins>;
+};
+
+&pwm1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pwm1_pins>;
};
&spi0 {
@@ -528,12 +533,22 @@ GPOEN_ENABLE,
};
};
- pwm_pins: pwm-0 {
- pwm-pins {
+ pwm0_pins: pwm0-0 {
+ pwm0-pins {
pinmux = <GPIOMUX(46, GPOUT_SYS_PWM_CHANNEL0,
GPOEN_SYS_PWM0_CHANNEL0,
- GPI_NONE)>,
- <GPIOMUX(59, GPOUT_SYS_PWM_CHANNEL1,
+ GPI_NONE)>;
+ bias-disable;
+ drive-strength = <12>;
+ input-disable;
+ input-schmitt-disable;
+ slew-rate = <0>;
+ };
+ };
+
+ pwm1_pins: pwm1-0 {
+ pwm1-pins {
+ pinmux = <GPIOMUX(59, GPOUT_SYS_PWM_CHANNEL1,
GPOEN_SYS_PWM0_CHANNEL1,
GPI_NONE)>;
bias-disable;
diff --git a/arch/riscv/boot/dts/starfive/jh7110-milkv-mars.dts b/arch/riscv/boot/dts/starfive/jh7110-milkv-mars.dts
index 21873612d993..54013c70f4b4 100644
--- a/arch/riscv/boot/dts/starfive/jh7110-milkv-mars.dts
+++ b/arch/riscv/boot/dts/starfive/jh7110-milkv-mars.dts
@@ -68,7 +68,11 @@ &phy0 {
motorcomm,tx-clk-adj-enabled;
};
-&pwm {
+&pwm0 {
+ status = "okay";
+};
+
+&pwm1 {
status = "okay";
};
diff --git a/arch/riscv/boot/dts/starfive/jh7110-milkv-marscm.dtsi b/arch/riscv/boot/dts/starfive/jh7110-milkv-marscm.dtsi
index 025471061d43..31afac27b86d 100644
--- a/arch/riscv/boot/dts/starfive/jh7110-milkv-marscm.dtsi
+++ b/arch/riscv/boot/dts/starfive/jh7110-milkv-marscm.dtsi
@@ -87,7 +87,11 @@ &phy0 {
motorcomm,tx-clk-adj-enabled;
};
-&pwm {
+&pwm0 {
+ status = "okay";
+};
+
+&pwm1 {
status = "okay";
};
diff --git a/arch/riscv/boot/dts/starfive/jh7110-pine64-star64.dts b/arch/riscv/boot/dts/starfive/jh7110-pine64-star64.dts
index aec7ae3d1f5b..a9e82f25efde 100644
--- a/arch/riscv/boot/dts/starfive/jh7110-pine64-star64.dts
+++ b/arch/riscv/boot/dts/starfive/jh7110-pine64-star64.dts
@@ -95,7 +95,11 @@ &phy1 {
motorcomm,tx-clk-100-inverted;
};
-&pwm {
+&pwm0 {
+ status = "okay";
+};
+
+&pwm1 {
status = "okay";
};
diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-lite.dtsi b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-lite.dtsi
index f8797a666dbf..85b56a72dff7 100644
--- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-lite.dtsi
+++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-lite.dtsi
@@ -74,7 +74,11 @@ &phy0 {
tx-internal-delay-ps = <1500>;
};
-&pwm {
+&pwm0 {
+ status = "okay";
+};
+
+&pwm1 {
status = "okay";
};
diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
index edc8f4588133..35208f95cd3d 100644
--- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
+++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
@@ -73,7 +73,11 @@ &pcie1 {
status = "okay";
};
-&pwm {
+&pwm0 {
+ status = "okay";
+};
+
+&pwm1 {
status = "okay";
};
diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi
index 9c3e4598747e..82ea63f715b0 100644
--- a/arch/riscv/boot/dts/starfive/jh7110.dtsi
+++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi
@@ -846,9 +846,72 @@ i2stx1: i2s@120c0000 {
status = "disabled";
};
- pwm: pwm@120d0000 {
+ pwm0: pwm@120d0000 {
compatible = "starfive,jh7110-pwm", "opencores,pwm-v1";
- reg = <0x0 0x120d0000 0x0 0x10000>;
+ reg = <0x0 0x120d0000 0x0 0x10>;
+ clocks = <&syscrg JH7110_SYSCLK_PWM_APB>;
+ resets = <&syscrg JH7110_SYSRST_PWM_APB>;
+ #pwm-cells = <3>;
+ status = "disabled";
+ };
+
+ pwm1: pwm@120d0010 {
+ compatible = "starfive,jh7110-pwm", "opencores,pwm-v1";
+ reg = <0x0 0x120d0010 0x0 0x10>;
+ clocks = <&syscrg JH7110_SYSCLK_PWM_APB>;
+ resets = <&syscrg JH7110_SYSRST_PWM_APB>;
+ #pwm-cells = <3>;
+ status = "disabled";
+ };
+
+ pwm2: pwm@120d0020 {
+ compatible = "starfive,jh7110-pwm", "opencores,pwm-v1";
+ reg = <0x0 0x120d0020 0x0 0x10>;
+ clocks = <&syscrg JH7110_SYSCLK_PWM_APB>;
+ resets = <&syscrg JH7110_SYSRST_PWM_APB>;
+ #pwm-cells = <3>;
+ status = "disabled";
+ };
+
+ pwm3: pwm@120d0030 {
+ compatible = "starfive,jh7110-pwm", "opencores,pwm-v1";
+ reg = <0x0 0x120d0030 0x0 0x10>;
+ clocks = <&syscrg JH7110_SYSCLK_PWM_APB>;
+ resets = <&syscrg JH7110_SYSRST_PWM_APB>;
+ #pwm-cells = <3>;
+ status = "disabled";
+ };
+
+ pwm4: pwm@120d8000 {
+ compatible = "starfive,jh7110-pwm", "opencores,pwm-v1";
+ reg = <0x0 0x120d8000 0x0 0x10>;
+ clocks = <&syscrg JH7110_SYSCLK_PWM_APB>;
+ resets = <&syscrg JH7110_SYSRST_PWM_APB>;
+ #pwm-cells = <3>;
+ status = "disabled";
+ };
+
+ pwm5: pwm@120d8010 {
+ compatible = "starfive,jh7110-pwm", "opencores,pwm-v1";
+ reg = <0x0 0x120d8010 0x0 0x10>;
+ clocks = <&syscrg JH7110_SYSCLK_PWM_APB>;
+ resets = <&syscrg JH7110_SYSRST_PWM_APB>;
+ #pwm-cells = <3>;
+ status = "disabled";
+ };
+
+ pwm6: pwm@120d8020 {
+ compatible = "starfive,jh7110-pwm", "opencores,pwm-v1";
+ reg = <0x0 0x120d8020 0x0 0x10>;
+ clocks = <&syscrg JH7110_SYSCLK_PWM_APB>;
+ resets = <&syscrg JH7110_SYSRST_PWM_APB>;
+ #pwm-cells = <3>;
+ status = "disabled";
+ };
+
+ pwm7: pwm@120d8030 {
+ compatible = "starfive,jh7110-pwm", "opencores,pwm-v1";
+ reg = <0x0 0x120d8030 0x0 0x10>;
clocks = <&syscrg JH7110_SYSCLK_PWM_APB>;
resets = <&syscrg JH7110_SYSRST_PWM_APB>;
#pwm-cells = <3>;
--
2.43.2
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH v19 3/3] pwm: Add OpenCores PTC PWM driver
2026-06-15 15:57 [PATCH v19 0/3] Add OpenCores PTC PWM support Hal Feng
2026-06-15 15:57 ` [PATCH v19 1/3] dt-bindings: pwm: opencores: Update compatibles, examples and maintainers Hal Feng
2026-06-15 15:57 ` [PATCH v19 2/3] riscv: dts: starfive: Correct pwm nodes Hal Feng
@ 2026-06-15 15:57 ` Hal Feng
2026-06-15 16:25 ` sashiko-bot
2 siblings, 1 reply; 7+ messages in thread
From: Hal Feng @ 2026-06-15 15:57 UTC (permalink / raw)
To: Uwe Kleine-König, Philipp Zabel, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Emil Renner Berthing,
Palmer Dabbelt, Paul Walmsley, Albert Ou
Cc: Hal Feng, linux-pwm, devicetree, linux-riscv, linux-kernel
Add PWM driver for OpenCores PTC IP core.
Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
---
MAINTAINERS | 6 +
drivers/pwm/Kconfig | 12 ++
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-ocores.c | 312 +++++++++++++++++++++++++++++++++++++++
4 files changed, 331 insertions(+)
create mode 100644 drivers/pwm/pwm-ocores.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 0d94420eae3d..b6a004f2e6c6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -20035,6 +20035,12 @@ F: Documentation/i2c/busses/i2c-ocores.rst
F: drivers/i2c/busses/i2c-ocores.c
F: include/linux/platform_data/i2c-ocores.h
+OPENCORES PWM DRIVER
+M: Hal Feng <hal.feng@starfivetech.com>
+S: Supported
+F: Documentation/devicetree/bindings/pwm/opencores,pwm.yaml
+F: drivers/pwm/pwm-ocores.c
+
OPENRISC ARCHITECTURE
M: Jonas Bonn <jonas@southpole.se>
M: Stefan Kristiansson <stefan.kristiansson@saunalahti.fi>
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 6f3147518376..dd7f3bf5c3eb 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -534,6 +534,18 @@ config PWM_NTXEC
controller found in certain e-book readers designed by the original
design manufacturer Netronix.
+config PWM_OCORES
+ tristate "OpenCores PTC PWM support"
+ depends on HAS_IOMEM && OF
+ depends on COMMON_CLK
+ depends on ARCH_STARFIVE || COMPILE_TEST
+ help
+ PWM driver for OpenCores PTC IP core.
+ For details see https://opencores.org/projects/ptc.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-ocores.
+
config PWM_OMAP_DMTIMER
tristate "OMAP Dual-Mode Timer PWM support"
depends on OF
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 0dc0d2b69025..2d47bad7bd74 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_PWM_MICROCHIP_CORE) += pwm-microchip-core.o
obj-$(CONFIG_PWM_MTK_DISP) += pwm-mtk-disp.o
obj-$(CONFIG_PWM_MXS) += pwm-mxs.o
obj-$(CONFIG_PWM_NTXEC) += pwm-ntxec.o
+obj-$(CONFIG_PWM_OCORES) += pwm-ocores.o
obj-$(CONFIG_PWM_OMAP_DMTIMER) += pwm-omap-dmtimer.o
obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o
obj-$(CONFIG_PWM_PXA) += pwm-pxa.o
diff --git a/drivers/pwm/pwm-ocores.c b/drivers/pwm/pwm-ocores.c
new file mode 100644
index 000000000000..b126933d9922
--- /dev/null
+++ b/drivers/pwm/pwm-ocores.c
@@ -0,0 +1,312 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * OpenCores PTC PWM Driver
+ *
+ * https://opencores.org/projects/ptc
+ *
+ * Copyright (C) 2018-2026 StarFive Technology Co., Ltd.
+ *
+ * Limitations:
+ * - The hardware only supports inverted polarity.
+ * - The hardware minimum period / duty_cycle of PWM is (1 / pwm_apb clock frequency).
+ * - The hardware maximum period / duty_cycle of PWM is (U32_MAX / pwm_apb clock frequency).
+ * - The output is immediately set to low when the module is disabled.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/pwm.h>
+#include <linux/reset.h>
+
+#define OCPWM_HRC 0x4
+#define OCPWM_LRC 0x8
+#define OCPWM_CTRL 0xC
+
+#define OCPWM_CTRL_EN BIT(0)
+#define OCPWM_CTRL_OE BIT(3)
+#define OCPWM_CTRL_RST BIT(7)
+
+#define OCPWM_NUM_SAVED_REGS 3
+
+struct ocores_pwm_device {
+ void __iomem *base;
+ struct clk *clk;
+ unsigned long clk_rate;
+ struct reset_control *rst;
+ u32 saved_regs[OCPWM_NUM_SAVED_REGS];
+};
+
+static int ocores_pwm_get_state(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct ocores_pwm_device *ddata = pwmchip_get_drvdata(chip);
+ u32 period_data, duty_data, ctrl_data;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(pwmchip_parent(chip));
+ if (ret < 0)
+ return ret;
+
+ period_data = readl(ddata->base + OCPWM_LRC);
+ duty_data = readl(ddata->base + OCPWM_HRC);
+ ctrl_data = readl(ddata->base + OCPWM_CTRL);
+
+ state->period = DIV_ROUND_UP_ULL((u64)period_data * NSEC_PER_SEC, ddata->clk_rate);
+ state->duty_cycle = DIV_ROUND_UP_ULL((u64)duty_data * NSEC_PER_SEC, ddata->clk_rate);
+ if (state->duty_cycle > state->period)
+ state->duty_cycle = state->period;
+
+ state->polarity = PWM_POLARITY_INVERSED;
+ state->enabled = (ctrl_data & OCPWM_CTRL_EN) ? true : false;
+
+ pm_runtime_put(pwmchip_parent(chip));
+
+ return 0;
+}
+
+static int ocores_pwm_apply(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct ocores_pwm_device *ddata = pwmchip_get_drvdata(chip);
+ bool was_enabled = pwm_is_enabled(pwm);
+ u64 period_data, duty_data;
+ int ret;
+
+ if (state->polarity != PWM_POLARITY_INVERSED)
+ return -EINVAL;
+
+ if (state->enabled) {
+ if (!was_enabled) {
+ ret = pm_runtime_resume_and_get(pwmchip_parent(chip));
+ if (ret < 0)
+ return ret;
+ }
+ } else {
+ if (was_enabled) {
+ writel(0, ddata->base + OCPWM_CTRL);
+ pm_runtime_put(pwmchip_parent(chip));
+ }
+ return 0;
+ }
+
+ writel(0, ddata->base + OCPWM_CTRL);
+ writel(OCPWM_CTRL_RST, ddata->base + OCPWM_CTRL);
+
+ period_data = mul_u64_u32_div(state->period, ddata->clk_rate, NSEC_PER_SEC);
+ if (period_data > U32_MAX)
+ period_data = U32_MAX;
+
+ duty_data = mul_u64_u32_div(state->duty_cycle, ddata->clk_rate, NSEC_PER_SEC);
+ if (duty_data > U32_MAX)
+ duty_data = U32_MAX;
+
+ if (!period_data || !duty_data) {
+ if (!was_enabled)
+ pm_runtime_put(pwmchip_parent(chip));
+ return -EINVAL;
+ }
+
+ writel(period_data, ddata->base + OCPWM_LRC);
+ writel(duty_data, ddata->base + OCPWM_HRC);
+ writel(OCPWM_CTRL_OE | OCPWM_CTRL_EN, ddata->base + OCPWM_CTRL);
+
+ return 0;
+}
+
+static void ocores_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct ocores_pwm_device *ddata = pwmchip_get_drvdata(chip);
+
+ if (pwm_is_enabled(pwm)) {
+ writel(0, ddata->base + OCPWM_CTRL);
+ pm_runtime_put_sync(pwmchip_parent(chip));
+ }
+}
+
+static const struct pwm_ops ocores_pwm_ops = {
+ .get_state = ocores_pwm_get_state,
+ .apply = ocores_pwm_apply,
+ .free = ocores_pwm_free,
+};
+
+static int ocores_pwm_runtime_suspend(struct device *dev)
+{
+ struct ocores_pwm_device *ddata = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(ddata->clk);
+
+ return 0;
+}
+
+static int ocores_pwm_runtime_resume(struct device *dev)
+{
+ struct ocores_pwm_device *ddata = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(ddata->clk);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable clock\n");
+
+ return 0;
+}
+
+static int __maybe_unused ocores_pwm_suspend(struct device *dev)
+{
+ struct ocores_pwm_device *ddata = dev_get_drvdata(dev);
+ int ret, i;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < OCPWM_NUM_SAVED_REGS; i++)
+ ddata->saved_regs[i] = readl(ddata->base + 4 + 4 * i);
+
+ pm_runtime_put_sync(dev);
+
+ return pm_runtime_force_suspend(dev);
+}
+
+static int __maybe_unused ocores_pwm_resume(struct device *dev)
+{
+ struct ocores_pwm_device *ddata = dev_get_drvdata(dev);
+ int ret, i;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ writel(0, ddata->base + OCPWM_CTRL);
+ writel(OCPWM_CTRL_RST, ddata->base + OCPWM_CTRL);
+ for (i = 0; i < OCPWM_NUM_SAVED_REGS; i++)
+ writel(ddata->saved_regs[i], ddata->base + 4 + 4 * i);
+
+ pm_runtime_put_sync(dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops ocores_pwm_pm_ops = {
+ RUNTIME_PM_OPS(ocores_pwm_runtime_suspend,
+ ocores_pwm_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(ocores_pwm_suspend, ocores_pwm_resume)
+};
+
+static void ocores_pwm_pm_disable(void *data)
+{
+ struct device *dev = data;
+ struct ocores_pwm_device *ddata = dev_get_drvdata(dev);
+
+ pm_runtime_disable(dev);
+
+ if (!pm_runtime_status_suspended(dev)) {
+ /* Balance probe's pm_runtime_get_noresume() for bootloader-enabled PWM. */
+ if (readl(ddata->base + OCPWM_CTRL) & OCPWM_CTRL_EN)
+ pm_runtime_put_noidle(dev);
+
+ ocores_pwm_runtime_suspend(dev);
+ }
+
+ reset_control_assert(ddata->rst);
+}
+
+static int ocores_pwm_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ocores_pwm_device *ddata;
+ struct pwm_chip *chip;
+ int ret;
+
+ chip = devm_pwmchip_alloc(dev, 1, sizeof(*ddata));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+
+ chip->ops = &ocores_pwm_ops;
+ ddata = pwmchip_get_drvdata(chip);
+
+ ddata->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(ddata->base))
+ return dev_err_probe(dev, PTR_ERR(ddata->base),
+ "Failed to map IO resources\n");
+
+ ddata->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(ddata->clk))
+ return dev_err_probe(dev, PTR_ERR(ddata->clk),
+ "Failed to get clock\n");
+
+ ddata->clk_rate = clk_get_rate(ddata->clk);
+ if (!ddata->clk_rate || ddata->clk_rate > NSEC_PER_SEC)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid clock rate: %lu\n", ddata->clk_rate);
+
+ ddata->rst = devm_reset_control_get_optional_shared(dev, NULL);
+ if (IS_ERR(ddata->rst))
+ return dev_err_probe(dev, PTR_ERR(ddata->rst),
+ "Failed to get reset\n");
+
+ platform_set_drvdata(pdev, ddata);
+
+ ret = ocores_pwm_runtime_resume(dev);
+ if (ret)
+ return ret;
+
+ ret = reset_control_deassert(ddata->rst);
+ if (ret)
+ goto err_clk_disable;
+
+ ret = pm_runtime_set_active(dev);
+ if (ret)
+ goto err_reset_assert;
+
+ pm_runtime_get_noresume(dev);
+ pm_runtime_enable(dev);
+
+ if (!(readl(ddata->base + OCPWM_CTRL) & OCPWM_CTRL_EN))
+ pm_runtime_put_sync(dev);
+
+ ret = devm_add_action_or_reset(dev, ocores_pwm_pm_disable, dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add pm disable action\n");
+
+ ret = devm_pwmchip_add(dev, chip);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Could not register PWM chip\n");
+
+ return 0;
+
+err_reset_assert:
+ reset_control_assert(ddata->rst);
+err_clk_disable:
+ ocores_pwm_runtime_suspend(dev);
+ return dev_err_probe(dev, ret, "Failed to init pwm power\n");
+}
+
+static const struct of_device_id ocores_pwm_of_match[] = {
+ { .compatible = "opencores,pwm-v1" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ocores_pwm_of_match);
+
+static struct platform_driver ocores_pwm_driver = {
+ .probe = ocores_pwm_probe,
+ .driver = {
+ .name = "ocores-pwm",
+ .of_match_table = ocores_pwm_of_match,
+ .pm = pm_ptr(&ocores_pwm_pm_ops),
+ },
+};
+module_platform_driver(ocores_pwm_driver);
+
+MODULE_AUTHOR("Jieqin Chen");
+MODULE_AUTHOR("Hal Feng <hal.feng@starfivetech.com>");
+MODULE_DESCRIPTION("OpenCores PTC PWM driver");
+MODULE_LICENSE("GPL");
--
2.43.2
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH v19 3/3] pwm: Add OpenCores PTC PWM driver
2026-06-15 15:57 ` [PATCH v19 3/3] pwm: Add OpenCores PTC PWM driver Hal Feng
@ 2026-06-15 16:25 ` sashiko-bot
0 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-06-15 16:25 UTC (permalink / raw)
To: Hal Feng; +Cc: conor+dt, robh, devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Hardware state corruption and PM runtime leak in `ocores_pwm_apply` on invalid parameter request.
--
commit b3a12f20d0a4ac7f1d2dc7a3d103fe2acf522975
Author: Hal Feng <hal.feng@starfivetech.com>
Subject: pwm: Add OpenCores PTC PWM driver
This commit adds a PWM driver for the OpenCores PTC IP core.
> diff --git a/drivers/pwm/pwm-ocores.c b/drivers/pwm/pwm-ocores.c
> new file mode 100644
> --- /dev/null
> +++ b/drivers/pwm/pwm-ocores.c
[ ... ]
> +static int ocores_pwm_apply(struct pwm_chip *chip,
> + struct pwm_device *pwm,
> + const struct pwm_state *state)
> +{
> + struct ocores_pwm_device *ddata = pwmchip_get_drvdata(chip);
> + bool was_enabled = pwm_is_enabled(pwm);
> + u64 period_data, duty_data;
> + int ret;
> +
> + if (state->polarity != PWM_POLARITY_INVERSED)
> + return -EINVAL;
> +
> + if (state->enabled) {
> + if (!was_enabled) {
> + ret = pm_runtime_resume_and_get(pwmchip_parent(chip));
> + if (ret < 0)
> + return ret;
> + }
> + } else {
> + if (was_enabled) {
> + writel(0, ddata->base + OCPWM_CTRL);
> + pm_runtime_put(pwmchip_parent(chip));
> + }
> + return 0;
> + }
> +
> + writel(0, ddata->base + OCPWM_CTRL);
> + writel(OCPWM_CTRL_RST, ddata->base + OCPWM_CTRL);
> +
> + period_data = mul_u64_u32_div(state->period, ddata->clk_rate, NSEC_PER_SEC);
> + if (period_data > U32_MAX)
> + period_data = U32_MAX;
> +
> + duty_data = mul_u64_u32_div(state->duty_cycle, ddata->clk_rate, NSEC_PER_SEC);
> + if (duty_data > U32_MAX)
> + duty_data = U32_MAX;
> +
> + if (!period_data || !duty_data) {
> + if (!was_enabled)
> + pm_runtime_put(pwmchip_parent(chip));
> + return -EINVAL;
[Severity: High]
Does stopping and resetting the hardware before validating the requested
parameters cause state corruption if the validation fails?
If period_data or duty_data evaluate to 0, the function returns -EINVAL.
Since the hardware was already stopped and reset, the framework's software
state will show the PWM as enabled, but the physical hardware will remain
disabled.
Additionally, if was_enabled is true, does this error path leak the PM
runtime reference?
Since OCPWM_CTRL_EN was prematurely cleared, ocores_pwm_pm_disable() will
later see the enable bit as 0 and skip its pm_runtime_put_noidle() call:
ocores_pwm_pm_disable() {
...
if (!pm_runtime_status_suspended(dev)) {
if (readl(ddata->base + OCPWM_CTRL) & OCPWM_CTRL_EN)
pm_runtime_put_noidle(dev);
...
}
> + }
> +
> + writel(period_data, ddata->base + OCPWM_LRC);
> + writel(duty_data, ddata->base + OCPWM_HRC);
> + writel(OCPWM_CTRL_OE | OCPWM_CTRL_EN, ddata->base + OCPWM_CTRL);
> +
> + return 0;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260615155759.129210-1-hal.feng@starfivetech.com?part=3
^ permalink raw reply [flat|nested] 7+ messages in thread