* [PATCH 0/2] pwm: clk-pwm: Add GPIO support for constant output levels
@ 2026-04-06 15:50 Xilin Wu
2026-04-06 15:50 ` [PATCH 1/2] dt-bindings: pwm: clk-pwm: add optional GPIO and pinctrl properties Xilin Wu
2026-04-06 15:50 ` [PATCH 2/2] pwm: clk-pwm: add GPIO and pinctrl support for constant output levels Xilin Wu
0 siblings, 2 replies; 4+ messages in thread
From: Xilin Wu @ 2026-04-06 15:50 UTC (permalink / raw)
To: Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Nikita Travkin
Cc: linux-pwm, devicetree, linux-kernel, linux-arm-msm, Xilin Wu
The clk-pwm driver uses a clock with duty cycle control to generate
PWM output. However, when the PWM is disabled or a 0%/100% duty cycle
is requested, the clock must be stopped, and the resulting pin level
is undefined and hardware-dependent.
This series adds optional GPIO and pinctrl support to the clk-pwm
driver. When a GPIO and pinctrl states ("default" for clock mux,
"gpio" for GPIO mode) are provided in the device tree, the driver
switches the pin to GPIO mode and drives a deterministic output level
for disabled/0%/100% states. For normal PWM output the pin is switched
back to its clock function mux. If no GPIO is provided, the driver
falls back to the original clock-only behavior.
Signed-off-by: Xilin Wu <sophon@radxa.com>
---
Xilin Wu (2):
dt-bindings: pwm: clk-pwm: add optional GPIO and pinctrl properties
pwm: clk-pwm: add GPIO and pinctrl support for constant output levels
Documentation/devicetree/bindings/pwm/clk-pwm.yaml | 36 ++++++++++-
drivers/pwm/pwm-clk.c | 72 ++++++++++++++++++++--
2 files changed, 101 insertions(+), 7 deletions(-)
---
base-commit: 2febe6e6ee6e34c7754eff3c4d81aa7b0dcb7979
change-id: 20260406-clk-pwm-gpio-7f63b38908a5
Best regards,
--
Xilin Wu <sophon@radxa.com>
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 1/2] dt-bindings: pwm: clk-pwm: add optional GPIO and pinctrl properties
2026-04-06 15:50 [PATCH 0/2] pwm: clk-pwm: Add GPIO support for constant output levels Xilin Wu
@ 2026-04-06 15:50 ` Xilin Wu
2026-04-06 15:50 ` [PATCH 2/2] pwm: clk-pwm: add GPIO and pinctrl support for constant output levels Xilin Wu
1 sibling, 0 replies; 4+ messages in thread
From: Xilin Wu @ 2026-04-06 15:50 UTC (permalink / raw)
To: Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Nikita Travkin
Cc: linux-pwm, devicetree, linux-kernel, linux-arm-msm, Xilin Wu
The clk-pwm driver cannot produce constant output levels (0% or 100%
duty cycle, or disabled state) through the clock hardware alone - the
actual pin level when the clock is off is undefined and
hardware-dependent.
Document optional gpios, pinctrl-names, pinctrl-0, and pinctrl-1
properties that allow the driver to switch the pin between clock
function mux (for normal PWM output) and GPIO mode (to drive a
deterministic constant level).
Signed-off-by: Xilin Wu <sophon@radxa.com>
---
Documentation/devicetree/bindings/pwm/clk-pwm.yaml | 36 +++++++++++++++++++++-
1 file changed, 35 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/pwm/clk-pwm.yaml b/Documentation/devicetree/bindings/pwm/clk-pwm.yaml
index ec1768291503..2a0e3e02d27b 100644
--- a/Documentation/devicetree/bindings/pwm/clk-pwm.yaml
+++ b/Documentation/devicetree/bindings/pwm/clk-pwm.yaml
@@ -15,6 +15,11 @@ description: |
It's often possible to control duty-cycle of such clocks which makes them
suitable for generating PWM signal.
+ Optionally, a GPIO and pinctrl states can be provided. When a constant
+ output level is needed (0%, 100%, or disabled), the pin is switched to
+ GPIO mode to drive the level directly. For normal PWM output the pin is
+ switched back to its clock function mux.
+
allOf:
- $ref: pwm.yaml#
@@ -29,6 +34,26 @@ properties:
"#pwm-cells":
const: 2
+ gpios:
+ description:
+ Optional GPIO used to drive a constant level when the PWM output is
+ disabled or set to 0% / 100% duty cycle. When provided, pinctrl states
+ "default" (clock mux) and "gpio" must also be defined.
+ maxItems: 1
+
+ pinctrl-names: true
+
+ pinctrl-0:
+ description: Pin configuration for clock function mux (normal PWM).
+ maxItems: 1
+
+ pinctrl-1:
+ description: Pin configuration for GPIO mode (constant level output).
+ maxItems: 1
+
+dependencies:
+ gpios: [ pinctrl-0, pinctrl-1 ]
+
unevaluatedProperties: false
required:
@@ -41,6 +66,15 @@ examples:
compatible = "clk-pwm";
#pwm-cells = <2>;
clocks = <&gcc 0>;
- pinctrl-names = "default";
+ };
+
+ - |
+ pwm {
+ compatible = "clk-pwm";
+ #pwm-cells = <2>;
+ clocks = <&gcc 0>;
+ pinctrl-names = "default", "gpio";
pinctrl-0 = <&pwm_clk_flash_default>;
+ pinctrl-1 = <&pwm_clk_flash_gpio>;
+ gpios = <&tlmm 32 0>;
};
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/2] pwm: clk-pwm: add GPIO and pinctrl support for constant output levels
2026-04-06 15:50 [PATCH 0/2] pwm: clk-pwm: Add GPIO support for constant output levels Xilin Wu
2026-04-06 15:50 ` [PATCH 1/2] dt-bindings: pwm: clk-pwm: add optional GPIO and pinctrl properties Xilin Wu
@ 2026-04-06 15:50 ` Xilin Wu
2026-04-06 16:20 ` Nikita Travkin
1 sibling, 1 reply; 4+ messages in thread
From: Xilin Wu @ 2026-04-06 15:50 UTC (permalink / raw)
To: Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Nikita Travkin
Cc: linux-pwm, devicetree, linux-kernel, linux-arm-msm, Xilin Wu
The clk-pwm driver cannot guarantee a defined output level when the
PWM is disabled or when 0%/100% duty cycle is requested, because the
pin state when the clock is stopped is hardware-dependent.
Add optional GPIO and pinctrl support: when a GPIO descriptor and
pinctrl states ("default" for clock mux, "gpio" for GPIO mode) are
provided in the device tree, the driver switches the pin to GPIO mode
and drives the appropriate level for disabled/0%/100% states. For
normal PWM output, the pin is switched back to its clock function mux.
If no GPIO is provided, the driver falls back to the original
clock-only behavior.
Signed-off-by: Xilin Wu <sophon@radxa.com>
---
drivers/pwm/pwm-clk.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 66 insertions(+), 6 deletions(-)
diff --git a/drivers/pwm/pwm-clk.c b/drivers/pwm/pwm-clk.c
index f8f5af57acba..99821fae54e7 100644
--- a/drivers/pwm/pwm-clk.c
+++ b/drivers/pwm/pwm-clk.c
@@ -10,12 +10,15 @@
* Limitations:
* - Due to the fact that exact behavior depends on the underlying
* clock driver, various limitations are possible.
- * - Underlying clock may not be able to give 0% or 100% duty cycle
- * (constant off or on), exact behavior will depend on the clock.
- * - When the PWM is disabled, the clock will be disabled as well,
- * line state will depend on the clock.
* - The clk API doesn't expose the necessary calls to implement
* .get_state().
+ *
+ * Optionally, a GPIO descriptor and pinctrl states ("default" and
+ * "gpio") can be provided. When a constant output level is needed
+ * (0% duty, 100% duty, or disabled), the driver switches the pin to
+ * GPIO mode and drives the appropriate level. For normal PWM output
+ * the pin is switched back to its clock function mux. If no GPIO is
+ * provided, the driver falls back to the original clock-only behavior.
*/
#include <linux/kernel.h>
@@ -25,11 +28,17 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/pwm.h>
struct pwm_clk_chip {
struct clk *clk;
bool clk_enabled;
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *pins_default; /* clock function mux */
+ struct pinctrl_state *pins_gpio; /* GPIO mode */
+ struct gpio_desc *gpiod;
};
static inline struct pwm_clk_chip *to_pwm_clk_chip(struct pwm_chip *chip)
@@ -45,14 +54,36 @@ static int pwm_clk_apply(struct pwm_chip *chip, struct pwm_device *pwm,
u32 rate;
u64 period = state->period;
u64 duty_cycle = state->duty_cycle;
+ bool constant_level = false;
+ int gpio_value = 0;
if (!state->enabled) {
- if (pwm->state.enabled) {
+ constant_level = true;
+ gpio_value = 0;
+ } else if (state->duty_cycle == 0) {
+ constant_level = true;
+ gpio_value = (state->polarity == PWM_POLARITY_INVERSED) ? 1 : 0;
+ } else if (state->duty_cycle >= state->period) {
+ constant_level = true;
+ gpio_value = (state->polarity == PWM_POLARITY_INVERSED) ? 0 : 1;
+ }
+
+ if (constant_level) {
+ if (pcchip->gpiod) {
+ pinctrl_select_state(pcchip->pinctrl, pcchip->pins_gpio);
+ gpiod_direction_output(pcchip->gpiod, gpio_value);
+ }
+ if (pcchip->clk_enabled) {
clk_disable(pcchip->clk);
pcchip->clk_enabled = false;
}
return 0;
- } else if (!pwm->state.enabled) {
+ }
+
+ if (pcchip->gpiod)
+ pinctrl_select_state(pcchip->pinctrl, pcchip->pins_default);
+
+ if (!pcchip->clk_enabled) {
ret = clk_enable(pcchip->clk);
if (ret)
return ret;
@@ -97,6 +128,35 @@ static int pwm_clk_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, PTR_ERR(pcchip->clk),
"Failed to get clock\n");
+ pcchip->pinctrl = devm_pinctrl_get(&pdev->dev);
+ if (IS_ERR(pcchip->pinctrl)) {
+ ret = PTR_ERR(pcchip->pinctrl);
+ pcchip->pinctrl = NULL;
+ if (ret == -EPROBE_DEFER)
+ return ret;
+ } else {
+ pcchip->pins_default = pinctrl_lookup_state(pcchip->pinctrl,
+ PINCTRL_STATE_DEFAULT);
+ pcchip->pins_gpio = pinctrl_lookup_state(pcchip->pinctrl,
+ "gpio");
+ if (IS_ERR(pcchip->pins_default) || IS_ERR(pcchip->pins_gpio))
+ pcchip->pinctrl = NULL;
+ }
+
+ /*
+ * Switch to GPIO pinctrl state before requesting the GPIO.
+ * The driver core has already applied the "default" state, which
+ * muxes the pin to the clock function and claims it. We must
+ * release that claim first so that gpiolib can request the pin.
+ */
+ if (pcchip->pinctrl)
+ pinctrl_select_state(pcchip->pinctrl, pcchip->pins_gpio);
+
+ pcchip->gpiod = devm_gpiod_get_optional(&pdev->dev, NULL, GPIOD_ASIS);
+ if (IS_ERR(pcchip->gpiod))
+ return dev_err_probe(&pdev->dev, PTR_ERR(pcchip->gpiod),
+ "Failed to get gpio\n");
+
chip->ops = &pwm_clk_ops;
ret = pwmchip_add(chip);
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH 2/2] pwm: clk-pwm: add GPIO and pinctrl support for constant output levels
2026-04-06 15:50 ` [PATCH 2/2] pwm: clk-pwm: add GPIO and pinctrl support for constant output levels Xilin Wu
@ 2026-04-06 16:20 ` Nikita Travkin
0 siblings, 0 replies; 4+ messages in thread
From: Nikita Travkin @ 2026-04-06 16:20 UTC (permalink / raw)
To: Xilin Wu
Cc: Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-pwm, devicetree, linux-kernel, linux-arm-msm
Xilin Wu писал(а) 06.04.2026 20:50:
> The clk-pwm driver cannot guarantee a defined output level when the
> PWM is disabled or when 0%/100% duty cycle is requested, because the
> pin state when the clock is stopped is hardware-dependent.
>
> Add optional GPIO and pinctrl support: when a GPIO descriptor and
> pinctrl states ("default" for clock mux, "gpio" for GPIO mode) are
> provided in the device tree, the driver switches the pin to GPIO mode
> and drives the appropriate level for disabled/0%/100% states. For
> normal PWM output, the pin is switched back to its clock function mux.
>
> If no GPIO is provided, the driver falls back to the original
> clock-only behavior.
>
> Signed-off-by: Xilin Wu <sophon@radxa.com>
> ---
> drivers/pwm/pwm-clk.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++-----
> 1 file changed, 66 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/pwm/pwm-clk.c b/drivers/pwm/pwm-clk.c
> index f8f5af57acba..99821fae54e7 100644
> --- a/drivers/pwm/pwm-clk.c
> +++ b/drivers/pwm/pwm-clk.c
> @@ -10,12 +10,15 @@
> * Limitations:
> * - Due to the fact that exact behavior depends on the underlying
> * clock driver, various limitations are possible.
> - * - Underlying clock may not be able to give 0% or 100% duty cycle
> - * (constant off or on), exact behavior will depend on the clock.
> - * - When the PWM is disabled, the clock will be disabled as well,
> - * line state will depend on the clock.
nit: I think those limitations would still stand for existing
users, perhaps we could just add "... unless gpio pinctrl state
is supplied" to these two?
> * - The clk API doesn't expose the necessary calls to implement
> * .get_state().
> + *
> + * Optionally, a GPIO descriptor and pinctrl states ("default" and
> + * "gpio") can be provided. When a constant output level is needed
> + * (0% duty, 100% duty, or disabled), the driver switches the pin to
> + * GPIO mode and drives the appropriate level. For normal PWM output
> + * the pin is switched back to its clock function mux. If no GPIO is
> + * provided, the driver falls back to the original clock-only behavior.
> */
>
> #include <linux/kernel.h>
> @@ -25,11 +28,17 @@
> #include <linux/of.h>
> #include <linux/platform_device.h>
> #include <linux/clk.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/pinctrl/consumer.h>
> #include <linux/pwm.h>
>
> struct pwm_clk_chip {
> struct clk *clk;
> bool clk_enabled;
> + struct pinctrl *pinctrl;
> + struct pinctrl_state *pins_default; /* clock function mux */
> + struct pinctrl_state *pins_gpio; /* GPIO mode */
> + struct gpio_desc *gpiod;
> };
>
> static inline struct pwm_clk_chip *to_pwm_clk_chip(struct pwm_chip *chip)
> @@ -45,14 +54,36 @@ static int pwm_clk_apply(struct pwm_chip *chip, struct pwm_device *pwm,
> u32 rate;
> u64 period = state->period;
> u64 duty_cycle = state->duty_cycle;
> + bool constant_level = false;
> + int gpio_value = 0;
>
> if (!state->enabled) {
> - if (pwm->state.enabled) {
> + constant_level = true;
> + gpio_value = 0;
> + } else if (state->duty_cycle == 0) {
> + constant_level = true;
> + gpio_value = (state->polarity == PWM_POLARITY_INVERSED) ? 1 : 0;
> + } else if (state->duty_cycle >= state->period) {
> + constant_level = true;
> + gpio_value = (state->polarity == PWM_POLARITY_INVERSED) ? 0 : 1;
> + }
> +
> + if (constant_level) {
> + if (pcchip->gpiod) {
> + pinctrl_select_state(pcchip->pinctrl, pcchip->pins_gpio);
> + gpiod_direction_output(pcchip->gpiod, gpio_value);
Is this the same case as below where gpio state has to be set
before we can control it, or can we swap those so we first
put gpio into a known state and only then mux it to the pad?
Thanks for improving this driver,
Nikita
> + }
> + if (pcchip->clk_enabled) {
> clk_disable(pcchip->clk);
> pcchip->clk_enabled = false;
> }
> return 0;
> - } else if (!pwm->state.enabled) {
> + }
> +
> + if (pcchip->gpiod)
> + pinctrl_select_state(pcchip->pinctrl, pcchip->pins_default);
> +
> + if (!pcchip->clk_enabled) {
> ret = clk_enable(pcchip->clk);
> if (ret)
> return ret;
> @@ -97,6 +128,35 @@ static int pwm_clk_probe(struct platform_device *pdev)
> return dev_err_probe(&pdev->dev, PTR_ERR(pcchip->clk),
> "Failed to get clock\n");
>
> + pcchip->pinctrl = devm_pinctrl_get(&pdev->dev);
> + if (IS_ERR(pcchip->pinctrl)) {
> + ret = PTR_ERR(pcchip->pinctrl);
> + pcchip->pinctrl = NULL;
> + if (ret == -EPROBE_DEFER)
> + return ret;
> + } else {
> + pcchip->pins_default = pinctrl_lookup_state(pcchip->pinctrl,
> + PINCTRL_STATE_DEFAULT);
> + pcchip->pins_gpio = pinctrl_lookup_state(pcchip->pinctrl,
> + "gpio");
> + if (IS_ERR(pcchip->pins_default) || IS_ERR(pcchip->pins_gpio))
> + pcchip->pinctrl = NULL;
> + }
> +
> + /*
> + * Switch to GPIO pinctrl state before requesting the GPIO.
> + * The driver core has already applied the "default" state, which
> + * muxes the pin to the clock function and claims it. We must
> + * release that claim first so that gpiolib can request the pin.
> + */
> + if (pcchip->pinctrl)
> + pinctrl_select_state(pcchip->pinctrl, pcchip->pins_gpio);
> +
> + pcchip->gpiod = devm_gpiod_get_optional(&pdev->dev, NULL, GPIOD_ASIS);
> + if (IS_ERR(pcchip->gpiod))
> + return dev_err_probe(&pdev->dev, PTR_ERR(pcchip->gpiod),
> + "Failed to get gpio\n");
> +
> chip->ops = &pwm_clk_ops;
>
> ret = pwmchip_add(chip);
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-04-06 16:27 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-06 15:50 [PATCH 0/2] pwm: clk-pwm: Add GPIO support for constant output levels Xilin Wu
2026-04-06 15:50 ` [PATCH 1/2] dt-bindings: pwm: clk-pwm: add optional GPIO and pinctrl properties Xilin Wu
2026-04-06 15:50 ` [PATCH 2/2] pwm: clk-pwm: add GPIO and pinctrl support for constant output levels Xilin Wu
2026-04-06 16:20 ` Nikita Travkin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox