* [PATCH 0/2] pwm: add Axiado AX3000 PWM support
@ 2026-06-18 12:26 Petar Stepanovic
2026-06-18 12:26 ` [PATCH 1/2] dt-bindings: pwm: add Axiado AX3000 PWM Petar Stepanovic
2026-06-18 12:26 ` [PATCH 2/2] pwm: add Axiado AX3000 PWM driver Petar Stepanovic
0 siblings, 2 replies; 5+ messages in thread
From: Petar Stepanovic @ 2026-06-18 12:26 UTC (permalink / raw)
To: Akhila Kavi, Prasad Bolisetty, Uwe Kleine-König, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Harshit Shah
Cc: linux-pwm, devicetree, linux-arm-kernel, linux-kernel,
Petar Stepanovic
This series adds support for the PWM controller found on Axiado
AX3000 and AX3005 SoCs.
A new driver is needed because this PWM controller is a SoC-specific
hardware block used on Axiado SoCs. It has its own register layout,
enable control, period configuration, duty-cycle configuration, and
does not match any existing upstream PWM driver.
The controller provides configurable PWM output signals. The driver
exposes the controller through the Linux PWM framework and supports
period and duty-cycle configuration.
The driver converts the requested period and duty cycle from nanoseconds
to hardware clock cycles based on the input clock rate.
The datasheet is not publicly available. Public high-level product
information is available at:
https://axiado.com/products/#AX3080
The register definitions and programming sequence used by this driver
are based on Axiado internal SoC documentation.
Signed-off-by: Petar Stepanovic <pstepanovic@axiado.com>
---
Petar Stepanovic (2):
dt-bindings: pwm: add Axiado AX3000 PWM
pwm: add Axiado AX3000 PWM driver
.../devicetree/bindings/pwm/axiado,ax3000-pwm.yaml | 52 ++++++
MAINTAINERS | 9 +
drivers/pwm/Kconfig | 11 ++
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-axiado.c | 193 +++++++++++++++++++++
5 files changed, 266 insertions(+)
---
base-commit: 1d5dcaa3bd65f2e8c9baa14a393d3a2dc5db7524
change-id: 20260518-axiado-ax3000-pwm-cd7c346849f1
Best regards,
--
Petar Stepanovic <pstepanovic@axiado.com>
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH 1/2] dt-bindings: pwm: add Axiado AX3000 PWM 2026-06-18 12:26 [PATCH 0/2] pwm: add Axiado AX3000 PWM support Petar Stepanovic @ 2026-06-18 12:26 ` Petar Stepanovic 2026-06-22 12:50 ` Krzysztof Kozlowski 2026-06-18 12:26 ` [PATCH 2/2] pwm: add Axiado AX3000 PWM driver Petar Stepanovic 1 sibling, 1 reply; 5+ messages in thread From: Petar Stepanovic @ 2026-06-18 12:26 UTC (permalink / raw) To: Akhila Kavi, Prasad Bolisetty, Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Harshit Shah Cc: linux-pwm, devicetree, linux-arm-kernel, linux-kernel, Petar Stepanovic The Axiado AX3000 and AX3005 SoCs include PWM controllers that can be used to generate configurable PWM output signals. Signed-off-by: Petar Stepanovic <pstepanovic@axiado.com> --- .../devicetree/bindings/pwm/axiado,ax3000-pwm.yaml | 52 ++++++++++++++++++++++ MAINTAINERS | 8 ++++ 2 files changed, 60 insertions(+) diff --git a/Documentation/devicetree/bindings/pwm/axiado,ax3000-pwm.yaml b/Documentation/devicetree/bindings/pwm/axiado,ax3000-pwm.yaml new file mode 100644 index 000000000000..8e5fee8c499d --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/axiado,ax3000-pwm.yaml @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/axiado,ax3000-pwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Axiado AX3000 PWM controller + +maintainers: + - Petar Stepanovic <pstepanovic@axiado.com> + - Akhila Kavi <akavi@axiado.com> + - Prasad Bolisetty <pbolisetty@axiado.com> + +description: + The Axiado PWM controller found on AX3000 and AX3005 SoCs. + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + const: axiado,ax3000-pwm + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + const: pwm + + "#pwm-cells": + const: 2 + +required: + - compatible + - reg + - clocks + +additionalProperties: false + +examples: + - | + pwm@80801c00 { + compatible = "axiado,ax3000-pwm"; + reg = <0x80801c00 0x1000>; + #pwm-cells = <2>; + clocks = <&clk>; + clock-names = "pwm"; + }; + diff --git a/MAINTAINERS b/MAINTAINERS index b2040011a386..394c4a3527e8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4312,6 +4312,14 @@ S: Orphan F: Documentation/devicetree/bindings/sound/axentia,* F: sound/soc/atmel/tse850-pcm5142.c +AXIADO AX3000 PWM DRIVER +M: Petar Stepanovic <pstepanovic@axiado.com> +M: Akhila Kavi <akavi@axiado.com> +M: Prasad Bolisetty <pbolisetty@axiado.com> +L: linux-pwm@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/pwm/axiado,ax3000-pwm.yaml + AXIS ARTPEC ARM64 SoC SUPPORT M: Jesper Nilsson <jesper.nilsson@axis.com> M: Lars Persson <lars.persson@axis.com> -- 2.34.1 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 1/2] dt-bindings: pwm: add Axiado AX3000 PWM 2026-06-18 12:26 ` [PATCH 1/2] dt-bindings: pwm: add Axiado AX3000 PWM Petar Stepanovic @ 2026-06-22 12:50 ` Krzysztof Kozlowski 0 siblings, 0 replies; 5+ messages in thread From: Krzysztof Kozlowski @ 2026-06-22 12:50 UTC (permalink / raw) To: Petar Stepanovic, Akhila Kavi, Prasad Bolisetty, Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Harshit Shah Cc: linux-pwm, devicetree, linux-arm-kernel, linux-kernel On 18/06/2026 14:26, Petar Stepanovic wrote: > + > +description: > + The Axiado PWM controller found on AX3000 and AX3005 SoCs. > + > +allOf: > + - $ref: pwm.yaml# > + > +properties: > + compatible: > + const: axiado,ax3000-pwm Description mentions AX3005, but there is no ax3005 compatible here. This is confusing. > + > + reg: > + maxItems: 1 > + > + clocks: > + maxItems: 1 > + > + clock-names: > + const: pwm Drop clock-names, not really useful if it has block's name. > + > + "#pwm-cells": > + const: 2 > + > +required: > + - compatible > + - reg > + - clocks > + > +additionalProperties: false > + Best regards, Krzysztof ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 2/2] pwm: add Axiado AX3000 PWM driver 2026-06-18 12:26 [PATCH 0/2] pwm: add Axiado AX3000 PWM support Petar Stepanovic 2026-06-18 12:26 ` [PATCH 1/2] dt-bindings: pwm: add Axiado AX3000 PWM Petar Stepanovic @ 2026-06-18 12:26 ` Petar Stepanovic 2026-06-22 16:29 ` Uwe Kleine-König 1 sibling, 1 reply; 5+ messages in thread From: Petar Stepanovic @ 2026-06-18 12:26 UTC (permalink / raw) To: Akhila Kavi, Prasad Bolisetty, Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Harshit Shah Cc: linux-pwm, devicetree, linux-arm-kernel, linux-kernel, Petar Stepanovic The Axiado AX3000 and AX3005 SoCs include PWM controllers that can be used to generate configurable PWM output signals. Add a PWM driver with support for configuring period, duty cycle, and enable state through the Linux PWM framework. Signed-off-by: Petar Stepanovic <pstepanovic@axiado.com> --- MAINTAINERS | 1 + drivers/pwm/Kconfig | 11 +++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-axiado.c | 193 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 206 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 394c4a3527e8..db93fc235c32 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4319,6 +4319,7 @@ M: Prasad Bolisetty <pbolisetty@axiado.com> L: linux-pwm@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/pwm/axiado,ax3000-pwm.yaml +F: drivers/pwm/pwm-axiado.c AXIS ARTPEC ARM64 SoC SUPPORT M: Jesper Nilsson <jesper.nilsson@axis.com> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 6f3147518376..76f6c04b0e23 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -129,6 +129,17 @@ config PWM_ATMEL_TCB To compile this driver as a module, choose M here: the module will be called pwm-atmel-tcb. +config PWM_AXIADO + tristate "Axiado PWM support" + depends on ARCH_AXIADO || COMPILE_TEST + depends on HAS_IOMEM + help + PWM framework driver for the PWM controller found on Axiado + AX3000 and AX3005 SoCs. + + To compile this driver as a module, choose M here: the module + will be called pwm-axiado. + config PWM_AXI_PWMGEN tristate "Analog Devices AXI PWM generator" depends on MICROBLAZE || NIOS2 || ARCH_ZYNQ || ARCH_ZYNQMP || ARCH_INTEL_SOCFPGA || COMPILE_TEST diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 0dc0d2b69025..4466a29e780a 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_PWM_ARGON_FAN_HAT) += pwm-argon-fan-hat.o obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM) += pwm-atmel-hlcdc.o obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o +obj-$(CONFIG_PWM_AXIADO) += pwm-axiado.o obj-$(CONFIG_PWM_AXI_PWMGEN) += pwm-axi-pwmgen.o obj-$(CONFIG_PWM_BCM2835) += pwm-bcm2835.o obj-$(CONFIG_PWM_BCM_IPROC) += pwm-bcm-iproc.o diff --git a/drivers/pwm/pwm-axiado.c b/drivers/pwm/pwm-axiado.c new file mode 100644 index 000000000000..db197886c5c4 --- /dev/null +++ b/drivers/pwm/pwm-axiado.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2021-2026 Axiado Corporation. + */ + +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/math64.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> + +/* Register offsets */ +#define AX_PWM_CNTRL_REG 0x0000 +#define AX_PWM_PERIOD_REG 0x0004 +#define AX_PWM_HIGH_REG 0x0008 + +/* PWM channels */ +#define AX_PWM_NUM 1 + +/* Period and duty cycle limits */ +#define AX_PWM_PERIOD_MIN 2 +#define AX_PWM_PERIOD_MAX 0xfffffffeU +#define AX_PWM_DUTY_MIN 1 +#define AX_PWM_DUTY_MAX 0xfffffffdU + +/* Control register bits */ +#define AX_PWM_CTRL_ENABLE BIT(0) +#define AX_PWM_CTRL_DISABLE 0x0 + +struct axiado_pwm_chip { + struct clk *clk; + void __iomem *base; +}; + +static int axiado_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + u64 duty_ns, u64 period_ns) +{ + struct axiado_pwm_chip *axpwm = pwmchip_get_drvdata(chip); + unsigned long rate; + u64 period_cycles, duty_cycles; + + /* + * The hardware does not support a zero period, 0% duty cycle, or + * 100% duty cycle. The caller should handle 0% duty cycle by + * disabling the PWM. + */ + if (!period_ns || !duty_ns || duty_ns >= period_ns) + return -EINVAL; + + rate = clk_get_rate(axpwm->clk); + if (!rate) + return -EINVAL; + + period_cycles = mul_u64_u64_div_u64(period_ns, rate, NSEC_PER_SEC); + if (period_cycles < AX_PWM_PERIOD_MIN || + period_cycles > AX_PWM_PERIOD_MAX) + return -EINVAL; + + duty_cycles = mul_u64_u64_div_u64(duty_ns, rate, NSEC_PER_SEC); + if (duty_cycles < AX_PWM_DUTY_MIN || + duty_cycles > AX_PWM_DUTY_MAX) + return -EINVAL; + + if (duty_cycles >= period_cycles) + return -EINVAL; + + writel((u32)period_cycles, axpwm->base + AX_PWM_PERIOD_REG); + writel((u32)duty_cycles, axpwm->base + AX_PWM_HIGH_REG); + + return 0; +} + +static int axiado_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct axiado_pwm_chip *axpwm = pwmchip_get_drvdata(chip); + int ret; + + if (state->polarity != PWM_POLARITY_NORMAL) + return -EINVAL; + + if (!state->enabled || !state->duty_cycle) { + if (pwm->state.enabled) + writel(AX_PWM_CTRL_DISABLE, axpwm->base + AX_PWM_CNTRL_REG); + + return 0; + } + + ret = axiado_pwm_config(chip, pwm, state->duty_cycle, state->period); + if (ret) + return ret; + + if (!pwm->state.enabled) + writel(AX_PWM_CTRL_ENABLE, axpwm->base + AX_PWM_CNTRL_REG); + + return 0; +} + +static int axiado_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct axiado_pwm_chip *axpwm = pwmchip_get_drvdata(chip); + unsigned long rate; + u32 period_cycles; + u32 duty_cycles; + u32 ctrl; + + rate = clk_get_rate(axpwm->clk); + if (!rate) + return -EINVAL; + + ctrl = readl(axpwm->base + AX_PWM_CNTRL_REG); + period_cycles = readl(axpwm->base + AX_PWM_PERIOD_REG); + duty_cycles = readl(axpwm->base + AX_PWM_HIGH_REG); + + state->enabled = !!(ctrl & AX_PWM_CTRL_ENABLE); + state->period = mul_u64_u64_div_u64(period_cycles, NSEC_PER_SEC, rate); + state->duty_cycle = mul_u64_u64_div_u64(duty_cycles, NSEC_PER_SEC, rate); + state->polarity = PWM_POLARITY_NORMAL; + + return 0; +} + +static const struct pwm_ops axiado_pwm_ops = { + .get_state = axiado_pwm_get_state, + .apply = axiado_pwm_apply, +}; + +static void axiado_pwm_disable(void *data) +{ + struct axiado_pwm_chip *axpwm = data; + + writel(AX_PWM_CTRL_DISABLE, axpwm->base + AX_PWM_CNTRL_REG); +} + +static int axiado_pwm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct axiado_pwm_chip *axpwm; + struct pwm_chip *chip; + int ret; + + chip = devm_pwmchip_alloc(dev, AX_PWM_NUM, sizeof(*axpwm)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + + axpwm = pwmchip_get_drvdata(chip); + + axpwm->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(axpwm->base)) + return dev_err_probe(dev, PTR_ERR(axpwm->base), + "failed to map registers\n"); + + ret = devm_add_action_or_reset(dev, axiado_pwm_disable, axpwm); + if (ret) + return ret; + + + axpwm->clk = devm_clk_get_enabled(dev, "pwm"); + if (IS_ERR(axpwm->clk)) + return dev_err_probe(dev, PTR_ERR(axpwm->clk), + "failed to get/enable clock\n"); + + chip->ops = &axiado_pwm_ops; + + ret = devm_pwmchip_add(dev, chip); + if (ret) + return dev_err_probe(dev, ret, "failed to add PWM chip\n"); + + return 0; +} + +static const struct of_device_id axiado_pwm_match[] = { + { .compatible = "axiado,ax3000-pwm" }, + { } +}; +MODULE_DEVICE_TABLE(of, axiado_pwm_match); + +static struct platform_driver axiado_pwm_driver = { + .driver = { + .name = "axiado-pwm", + .of_match_table = axiado_pwm_match, + }, + .probe = axiado_pwm_probe, +}; + +module_platform_driver(axiado_pwm_driver); + +MODULE_AUTHOR("Axiado Corporation"); +MODULE_DESCRIPTION("Axiado PWM driver"); +MODULE_LICENSE("GPL"); -- 2.34.1 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 2/2] pwm: add Axiado AX3000 PWM driver 2026-06-18 12:26 ` [PATCH 2/2] pwm: add Axiado AX3000 PWM driver Petar Stepanovic @ 2026-06-22 16:29 ` Uwe Kleine-König 0 siblings, 0 replies; 5+ messages in thread From: Uwe Kleine-König @ 2026-06-22 16:29 UTC (permalink / raw) To: Petar Stepanovic Cc: Akhila Kavi, Prasad Bolisetty, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Harshit Shah, linux-pwm, devicetree, linux-arm-kernel, linux-kernel [-- Attachment #1: Type: text/plain, Size: 9646 bytes --] Hello Petar, Just a very high-level review: On Thu, Jun 18, 2026 at 05:26:57AM -0700, Petar Stepanovic wrote: > The Axiado AX3000 and AX3005 SoCs include PWM controllers that can be > used to generate configurable PWM output signals. > > Add a PWM driver with support for configuring period, duty cycle, and > enable state through the Linux PWM framework. > > Signed-off-by: Petar Stepanovic <pstepanovic@axiado.com> > --- > MAINTAINERS | 1 + > drivers/pwm/Kconfig | 11 +++ > drivers/pwm/Makefile | 1 + > drivers/pwm/pwm-axiado.c | 193 +++++++++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 206 insertions(+) > > diff --git a/MAINTAINERS b/MAINTAINERS > index 394c4a3527e8..db93fc235c32 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -4319,6 +4319,7 @@ M: Prasad Bolisetty <pbolisetty@axiado.com> > L: linux-pwm@vger.kernel.org > S: Maintained > F: Documentation/devicetree/bindings/pwm/axiado,ax3000-pwm.yaml > +F: drivers/pwm/pwm-axiado.c > > AXIS ARTPEC ARM64 SoC SUPPORT > M: Jesper Nilsson <jesper.nilsson@axis.com> > diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig > index 6f3147518376..76f6c04b0e23 100644 > --- a/drivers/pwm/Kconfig > +++ b/drivers/pwm/Kconfig > @@ -129,6 +129,17 @@ config PWM_ATMEL_TCB > To compile this driver as a module, choose M here: the module > will be called pwm-atmel-tcb. > > +config PWM_AXIADO > + tristate "Axiado PWM support" > + depends on ARCH_AXIADO || COMPILE_TEST > + depends on HAS_IOMEM > + help > + PWM framework driver for the PWM controller found on Axiado > + AX3000 and AX3005 SoCs. > + > + To compile this driver as a module, choose M here: the module > + will be called pwm-axiado. > + > config PWM_AXI_PWMGEN > tristate "Analog Devices AXI PWM generator" > depends on MICROBLAZE || NIOS2 || ARCH_ZYNQ || ARCH_ZYNQMP || ARCH_INTEL_SOCFPGA || COMPILE_TEST > diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile > index 0dc0d2b69025..4466a29e780a 100644 > --- a/drivers/pwm/Makefile > +++ b/drivers/pwm/Makefile > @@ -8,6 +8,7 @@ obj-$(CONFIG_PWM_ARGON_FAN_HAT) += pwm-argon-fan-hat.o > obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o > obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM) += pwm-atmel-hlcdc.o > obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o > +obj-$(CONFIG_PWM_AXIADO) += pwm-axiado.o > obj-$(CONFIG_PWM_AXI_PWMGEN) += pwm-axi-pwmgen.o > obj-$(CONFIG_PWM_BCM2835) += pwm-bcm2835.o > obj-$(CONFIG_PWM_BCM_IPROC) += pwm-bcm-iproc.o > diff --git a/drivers/pwm/pwm-axiado.c b/drivers/pwm/pwm-axiado.c > new file mode 100644 > index 000000000000..db197886c5c4 > --- /dev/null > +++ b/drivers/pwm/pwm-axiado.c > @@ -0,0 +1,193 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* > + * Copyright (c) 2021-2026 Axiado Corporation. Please add a Limitations paragraph here like the ones found in the newer driver files. It should answer: - Is a period completed on configuration change? - Is a period completed on disable? - How does the output behave when disabled? (Low? Inactive? Freeze? High-Z?) Also mention special properties, like being unable to set 0% or 100% relative duty. > + */ > + > +#include <linux/bits.h> > +#include <linux/clk.h> > +#include <linux/io.h> > +#include <linux/math64.h> > +#include <linux/module.h> > +#include <linux/platform_device.h> > +#include <linux/pwm.h> > + > +/* Register offsets */ > +#define AX_PWM_CNTRL_REG 0x0000 > +#define AX_PWM_PERIOD_REG 0x0004 > +#define AX_PWM_HIGH_REG 0x0008 > + > +/* PWM channels */ > +#define AX_PWM_NUM 1 This is only used once, and having chip = devm_pwmchip_alloc(dev, 1, sizeof(*axpwm)); below simplifies grepping for channel numbers. > + > +/* Period and duty cycle limits */ > +#define AX_PWM_PERIOD_MIN 2 > +#define AX_PWM_PERIOD_MAX 0xfffffffeU > +#define AX_PWM_DUTY_MIN 1 > +#define AX_PWM_DUTY_MAX 0xfffffffdU The U suffix is not needed for hex constants (AFAIK). > + > +/* Control register bits */ > +#define AX_PWM_CTRL_ENABLE BIT(0) > +#define AX_PWM_CTRL_DISABLE 0x0 > + > +struct axiado_pwm_chip { > + struct clk *clk; > + void __iomem *base; > +}; If you use axiado_pwm_ as prefix for structs and functions, please use AXIADO_PWM_ as prefix for #defines. > + > +static int axiado_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, > + u64 duty_ns, u64 period_ns) > +{ > + struct axiado_pwm_chip *axpwm = pwmchip_get_drvdata(chip); > + unsigned long rate; > + u64 period_cycles, duty_cycles; > + > + /* > + * The hardware does not support a zero period, 0% duty cycle, or > + * 100% duty cycle. The caller should handle 0% duty cycle by > + * disabling the PWM. > + */ > + if (!period_ns || !duty_ns || duty_ns >= period_ns) > + return -EINVAL; > + > + rate = clk_get_rate(axpwm->clk); > + if (!rate) > + return -EINVAL; > + > + period_cycles = mul_u64_u64_div_u64(period_ns, rate, NSEC_PER_SEC); > + if (period_cycles < AX_PWM_PERIOD_MIN || > + period_cycles > AX_PWM_PERIOD_MAX) > + return -EINVAL; > + > + duty_cycles = mul_u64_u64_div_u64(duty_ns, rate, NSEC_PER_SEC); > + if (duty_cycles < AX_PWM_DUTY_MIN || > + duty_cycles > AX_PWM_DUTY_MAX) > + return -EINVAL; > + > + if (duty_cycles >= period_cycles) > + return -EINVAL; > + > + writel((u32)period_cycles, axpwm->base + AX_PWM_PERIOD_REG); > + writel((u32)duty_cycles, axpwm->base + AX_PWM_HIGH_REG); > + > + return 0; > +} > + > +static int axiado_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, > + const struct pwm_state *state) > +{ > + struct axiado_pwm_chip *axpwm = pwmchip_get_drvdata(chip); > + int ret; > + > + if (state->polarity != PWM_POLARITY_NORMAL) > + return -EINVAL; > + > + if (!state->enabled || !state->duty_cycle) { > + if (pwm->state.enabled) > + writel(AX_PWM_CTRL_DISABLE, axpwm->base + AX_PWM_CNTRL_REG); > + > + return 0; > + } > + > + ret = axiado_pwm_config(chip, pwm, state->duty_cycle, state->period); > + if (ret) > + return ret; > + > + if (!pwm->state.enabled) Ideally check hardware state and not PWM internal variables. > + writel(AX_PWM_CTRL_ENABLE, axpwm->base + AX_PWM_CNTRL_REG); > + > + return 0; > +} > + > +static int axiado_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, > + struct pwm_state *state) > +{ > + struct axiado_pwm_chip *axpwm = pwmchip_get_drvdata(chip); > + unsigned long rate; > + u32 period_cycles; > + u32 duty_cycles; > + u32 ctrl; > + > + rate = clk_get_rate(axpwm->clk); > + if (!rate) > + return -EINVAL; > + > + ctrl = readl(axpwm->base + AX_PWM_CNTRL_REG); > + period_cycles = readl(axpwm->base + AX_PWM_PERIOD_REG); > + duty_cycles = readl(axpwm->base + AX_PWM_HIGH_REG); > + > + state->enabled = !!(ctrl & AX_PWM_CTRL_ENABLE); > + state->period = mul_u64_u64_div_u64(period_cycles, NSEC_PER_SEC, rate); > + state->duty_cycle = mul_u64_u64_div_u64(duty_cycles, NSEC_PER_SEC, rate); > + state->polarity = PWM_POLARITY_NORMAL; Please test your driver with PWM_DEBUG enabled, the rounding is wrong here. > + > + return 0; > +} > + > +static const struct pwm_ops axiado_pwm_ops = { > + .get_state = axiado_pwm_get_state, > + .apply = axiado_pwm_apply, Please implement the waveform callbacke instead of .get_state() and .apply() > +}; > + > +static void axiado_pwm_disable(void *data) > +{ > + struct axiado_pwm_chip *axpwm = data; > + > + writel(AX_PWM_CTRL_DISABLE, axpwm->base + AX_PWM_CNTRL_REG); > +} > + > +static int axiado_pwm_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct axiado_pwm_chip *axpwm; > + struct pwm_chip *chip; > + int ret; > + > + chip = devm_pwmchip_alloc(dev, AX_PWM_NUM, sizeof(*axpwm)); > + if (IS_ERR(chip)) > + return PTR_ERR(chip); > + > + axpwm = pwmchip_get_drvdata(chip); > + > + axpwm->base = devm_platform_ioremap_resource(pdev, 0); > + if (IS_ERR(axpwm->base)) > + return dev_err_probe(dev, PTR_ERR(axpwm->base), > + "failed to map registers\n"); Start error messages with a capital letter please. > + > + ret = devm_add_action_or_reset(dev, axiado_pwm_disable, axpwm); > + if (ret) > + return ret; This isn't supposed to happen. It's the responsibility of the consumer to disable the PWM before it's freed. > + > + Single empty line only. > + axpwm->clk = devm_clk_get_enabled(dev, "pwm"); > + if (IS_ERR(axpwm->clk)) > + return dev_err_probe(dev, PTR_ERR(axpwm->clk), > + "failed to get/enable clock\n"); Please ensure that the clk rate doesn't change while the PWM is enabled. Then you can cache the clk rate and set chip->atomic. > + > + chip->ops = &axiado_pwm_ops; > + > + ret = devm_pwmchip_add(dev, chip); > + if (ret) > + return dev_err_probe(dev, ret, "failed to add PWM chip\n"); > + > + return 0; > +} > + > +static const struct of_device_id axiado_pwm_match[] = { > + { .compatible = "axiado,ax3000-pwm" }, > + { } > +}; > +MODULE_DEVICE_TABLE(of, axiado_pwm_match); > + > +static struct platform_driver axiado_pwm_driver = { > + .driver = { > + .name = "axiado-pwm", > + .of_match_table = axiado_pwm_match, > + }, > + .probe = axiado_pwm_probe, > +}; > + > +module_platform_driver(axiado_pwm_driver); No empty line between the driver struct and the module_platform helper please. > + > +MODULE_AUTHOR("Axiado Corporation"); > +MODULE_DESCRIPTION("Axiado PWM driver"); > +MODULE_LICENSE("GPL"); Best regards Uwe [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 488 bytes --] ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-06-22 16:29 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-06-18 12:26 [PATCH 0/2] pwm: add Axiado AX3000 PWM support Petar Stepanovic 2026-06-18 12:26 ` [PATCH 1/2] dt-bindings: pwm: add Axiado AX3000 PWM Petar Stepanovic 2026-06-22 12:50 ` Krzysztof Kozlowski 2026-06-18 12:26 ` [PATCH 2/2] pwm: add Axiado AX3000 PWM driver Petar Stepanovic 2026-06-22 16:29 ` Uwe Kleine-König
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox