mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v5 0/2] pwm: add Axiado AX3000 PWM support
@ 2026-09-21  7:21 Petar Stepanovic
  2026-09-21  7:21 ` [PATCH v5 1/2] dt-bindings: pwm: add Axiado AX3000 PWM Petar Stepanovic
  2026-09-21  7:21 ` [PATCH v5 2/2] pwm: add Axiado AX3000 PWM driver Petar Stepanovic
  0 siblings, 2 replies; 5+ messages in thread
From: Petar Stepanovic @ 2026-09-21  7:21 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, openbmc,
	Petar Stepanovic, Conor Dooley

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>
---
Changes in v5:
- Use the hardware disable state directly instead of emulating a
  disabled
  PWM with the constant-low representation.
- Remove driver-side cached period and logically-disabled state.
- Handle 0% duty cycle using the hardware constant-low representation
  during waveform rounding.
- Decode the constant-low representation as 0% duty with a
  one-clock-cycle
  period on readback.
- Simplify waveform read/write handling and remove redundant validation.
- Update the driver limitations documentation.
- Link to v4: https://patch.msgid.link/20260908-axiado-ax3000-pwm-v4-0-19a03671cd5b@axiado.com

Changes in v4:
- Corrected the AX3005 compatible ordering to list
  axiado,ax3005-pwm before its axiado,ax3000-pwm fallback.
- Changed the disabled-state handling to program a constant-low
  waveform,
  because disabling the hardware controller drives the output high.
- Added software state tracking so readback reports this constant-low
  hardware representation as a logically disabled PWM.
- Link to v3: https://patch.msgid.link/20260811-axiado-ax3000-pwm-v3-0-6ceaccd812cf@axiado.com

Changes in v3:
- Express AX3005 compatibility with AX3000 using a fallback compatible.
- Update the Devicetree binding example accordingly.
- Add 0% duty-cycle support using the hardware constant-low encoding.
- Cache the rounded period for 0% duty-cycle waveform readback.
- Allow sub-cycle duty requests to round down to 0% instead of rounding
  up to one clock cycle.
- Normalize high-time values greater than the period to 100% duty.
- Drop the redundant AX3005 OF match entry and use the AX3000 fallback.
- Clarify comments and PWM hardware limitations.
- Link to v2: https://lore.kernel.org/r/20260723-axiado-ax3000-pwm-v2-0-22002f0b281d@axiado.com

Changes in v2:
- Migrated the driver from the legacy apply/get_state API to the new
  waveform ops API (round_waveform_tohw/fromhw, read/write_waveform).
- Cache the clock rate at probe and lock it with
  devm_clk_rate_exclusive_get() instead of reading it on every op.
- Improved rounding: clamp period/duty to hardware limits, reject
  non-zero duty offset, preserve exact 100% duty, round sub-cycle duty
  up to the minimum.
- Added a "Limitations" comment block documenting hardware constraints.
- Renamed macros from AX_PWM_* to AXIADO_PWM_* and dropped unused ones.
- DT binding: dropped clock-names, made #pwm-cells required, and
  updated the description and compatible list to cover both the AX3000
  and AX3005 SoCs.
- Added axiado,ax3005-pwm to the driver's OF device match table.
- Link to v1: https://lore.kernel.org/r/20260618-axiado-ax3000-pwm-v1-0-c9797a909414@axiado.com

To: Petar Stepanovic <pstepanovic@axiado.com>
To: Akhila Kavi <akavi@axiado.com>
To: Prasad Bolisetty <pbolisetty@axiado.com>
To: Uwe Kleine-König <ukleinek@kernel.org>
To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Conor Dooley <conor+dt@kernel.org>
To: Harshit Shah <hshah@axiado.com>
Cc: linux-pwm@vger.kernel.org
Cc: devicetree@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org

---
Petar Stepanovic (2):
      dt-bindings: pwm: add Axiado AX3000 PWM
      pwm: add Axiado AX3000 PWM driver

 .../devicetree/bindings/pwm/axiado,ax3000-pwm.yaml |  53 ++++
 MAINTAINERS                                        |   9 +
 drivers/pwm/Kconfig                                |  11 +
 drivers/pwm/Makefile                               |   1 +
 drivers/pwm/pwm-axiado.c                           | 277 +++++++++++++++++++++
 5 files changed, 351 insertions(+)
---
base-commit: 368d34807e6d04bb2089918383b58abf40e720a0
change-id: 20260518-axiado-ax3000-pwm-cd7c346849f1

Best regards,
--  
Petar Stepanovic <pstepanovic@axiado.com>


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

* [PATCH v5 1/2] dt-bindings: pwm: add Axiado AX3000 PWM
  2026-09-21  7:21 [PATCH v5 0/2] pwm: add Axiado AX3000 PWM support Petar Stepanovic
@ 2026-09-21  7:21 ` Petar Stepanovic
  2026-09-21 14:35   ` Uwe Kleine-König
  2026-09-21  7:21 ` [PATCH v5 2/2] pwm: add Axiado AX3000 PWM driver Petar Stepanovic
  1 sibling, 1 reply; 5+ messages in thread
From: Petar Stepanovic @ 2026-09-21  7:21 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, openbmc,
	Petar Stepanovic, Conor Dooley

The Axiado AX3000 and AX3005 SoCs include PWM controllers that can be
used to generate configurable PWM output signals.

Acked-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Petar Stepanovic <pstepanovic@axiado.com>
---
 .../devicetree/bindings/pwm/axiado,ax3000-pwm.yaml | 53 ++++++++++++++++++++++
 MAINTAINERS                                        |  8 ++++
 2 files changed, 61 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..3b73b233da86
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/axiado,ax3000-pwm.yaml
@@ -0,0 +1,53 @@
+# 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 the AX3000 and AX3005 SoCs.
+
+allOf:
+  - $ref: pwm.yaml#
+
+properties:
+  compatible:
+    oneOf:
+      - const: axiado,ax3000-pwm
+      - items:
+          - const: axiado,ax3005-pwm
+          - const: axiado,ax3000-pwm
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  "#pwm-cells":
+    const: 2
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - "#pwm-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    pwm@80801c00 {
+      compatible = "axiado,ax3000-pwm";
+      reg = <0x80801c00 0x1000>;
+      #pwm-cells = <2>;
+      clocks = <&clk>;
+    };
+
diff --git a/MAINTAINERS b/MAINTAINERS
index b2040011a386..9999480376cc 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:	Supported
+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	[flat|nested] 5+ messages in thread

* [PATCH v5 2/2] pwm: add Axiado AX3000 PWM driver
  2026-09-21  7:21 [PATCH v5 0/2] pwm: add Axiado AX3000 PWM support Petar Stepanovic
  2026-09-21  7:21 ` [PATCH v5 1/2] dt-bindings: pwm: add Axiado AX3000 PWM Petar Stepanovic
@ 2026-09-21  7:21 ` Petar Stepanovic
  2026-09-21 14:48   ` Uwe Kleine-König
  1 sibling, 1 reply; 5+ messages in thread
From: Petar Stepanovic @ 2026-09-21  7:21 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, openbmc,
	Petar Stepanovic

The Axiado AX3000 and AX3005 SoCs include a single-channel PWM
controller.

Add a driver supporting period, duty-cycle, and enable-state
configuration through the Linux PWM framework.

Disabling the hardware controller drives its output high. A 0% duty
cycle is implemented using the controller's constant-low encoding,
because a zero high time is interpreted by the hardware as a
constant-high output. The constant-low state is reported as a 0% duty
cycle with a one-clock-cycle period on readback.

AX3005 uses the same programming model as AX3000 and is supported
through the axiado,ax3000-pwm fallback compatible.

Signed-off-by: Petar Stepanovic <pstepanovic@axiado.com>
---
 MAINTAINERS              |   1 +
 drivers/pwm/Kconfig      |  11 ++
 drivers/pwm/Makefile     |   1 +
 drivers/pwm/pwm-axiado.c | 277 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 290 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 9999480376cc..14eea3bc4d4d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4319,6 +4319,7 @@ M:	Prasad Bolisetty <pbolisetty@axiado.com>
 L:	linux-pwm@vger.kernel.org
 S:	Supported
 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..781053e255f0
--- /dev/null
+++ b/drivers/pwm/pwm-axiado.c
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * SPDX-FileCopyrightText: 2021-2026 Axiado Corporation.
+ */
+
+/*
+ * Limitations:
+ * - Only normal polarity is supported.
+ * - The hardware has no shadow registers, so configuration changes take
+ *   effect immediately without waiting for the current period to complete.
+ * - The output is driven high while the hardware is disabled.
+ * - Supported period range: 2 through 0xfffffffe PWM input clock cycles;
+ *   0xffffffff is reserved by the hardware for a constant low output.
+ *   Longer periods are clamped to the maximum.
+ * - A 0% duty cycle is emitted using the constant low encoding because the
+ *   hardware interprets a zero high time as a constant high output. The
+ *   requested period is lost in that case and is reported as a single clock
+ *   cycle.
+ * - A 100% duty cycle is supported and emits a constant high output.
+ */
+
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/err.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 AXIADO_PWM_CTRL_REG	0x0000
+#define AXIADO_PWM_PERIOD_REG	0x0004
+#define AXIADO_PWM_HIGH_REG	0x0008
+
+/* Period and duty cycle limits */
+#define AXIADO_PWM_PERIOD_MIN	2
+#define AXIADO_PWM_PERIOD_MAX	0xfffffffe
+#define AXIADO_PWM_PERIOD_CONST_LOW	0xffffffff
+#define AXIADO_PWM_DUTY_MIN	1
+
+/* Control register bits */
+#define AXIADO_PWM_CTRL_ENABLE	BIT(0)
+
+struct axiado_pwm_chip {
+	void __iomem *base;
+	unsigned long rate;
+};
+
+struct axiado_pwm_waveform {
+	u32 period;
+	u32 duty;
+	bool enabled;
+};
+
+static int
+axiado_pwm_round_waveform_tohw(struct pwm_chip *chip,
+			       struct pwm_device *pwm,
+			       const struct pwm_waveform *wf,
+			       void *_wfhw)
+{
+	struct axiado_pwm_chip *axpwm = pwmchip_get_drvdata(chip);
+	struct axiado_pwm_waveform *wfhw = _wfhw;
+	u64 period;
+	u64 duty;
+	int ret = 0;
+
+	/* Encode a disabled request as a zeroed hardware waveform. */
+	if (!wf->period_length_ns) {
+		*wfhw = (struct axiado_pwm_waveform) { };
+
+		return 0;
+	}
+
+	period = mul_u64_u64_div_u64(wf->period_length_ns, axpwm->rate,
+				     NSEC_PER_SEC);
+	duty = mul_u64_u64_div_u64(wf->duty_length_ns, axpwm->rate,
+				   NSEC_PER_SEC);
+
+	if (!duty) {
+		/*
+		 * A zero high time makes the hardware emit a constant high
+		 * output, so use the constant low period encoding for a 0%
+		 * duty cycle. The high time must stay non-zero because it
+		 * takes precedence over that encoding. The requested period
+		 * is lost, .round_waveform_fromhw() reports a single clock
+		 * cycle instead.
+		 */
+		*wfhw = (struct axiado_pwm_waveform) {
+			.period = AXIADO_PWM_PERIOD_CONST_LOW,
+			.duty = AXIADO_PWM_DUTY_MIN,
+			.enabled = true,
+		};
+
+		/* That single cycle is longer than a sub-cycle request. */
+		return period ? 0 : 1;
+	}
+
+	if (period < AXIADO_PWM_PERIOD_MIN) {
+		period = AXIADO_PWM_PERIOD_MIN;
+		ret = 1;
+	} else if (period > AXIADO_PWM_PERIOD_MAX) {
+		period = AXIADO_PWM_PERIOD_MAX;
+	}
+
+	/*
+	 * Period clamping can leave the converted duty greater than the
+	 * final hardware period. In that case, clamp it to 100% duty.
+	 */
+	if (duty > period)
+		duty = period;
+
+	*wfhw = (struct axiado_pwm_waveform) {
+		.period = period,
+		.duty = duty,
+		.enabled = true,
+	};
+
+	return ret;
+}
+
+static int
+axiado_pwm_round_waveform_fromhw(struct pwm_chip *chip,
+				 struct pwm_device *pwm,
+				 const void *_wfhw,
+				 struct pwm_waveform *wf)
+{
+	struct axiado_pwm_chip *axpwm = pwmchip_get_drvdata(chip);
+	const struct axiado_pwm_waveform *wfhw = _wfhw;
+	u32 period = wfhw->period;
+	u32 duty = wfhw->duty;
+
+	if (!wfhw->enabled) {
+		*wf = (struct pwm_waveform) {
+			.period_length_ns = 0,
+		};
+
+		return 0;
+	}
+
+	/*
+	 * The constant low encoding doesn't hold a period. Report the
+	 * shortest one, the output is low for all of it either way.
+	 */
+	if (period == AXIADO_PWM_PERIOD_CONST_LOW) {
+		period = 1;
+		duty = 0;
+	} else if (duty > period) {
+		duty = period;
+	}
+
+	*wf = (struct pwm_waveform) {
+		.period_length_ns =
+			mul_u64_u64_div_u64_roundup(period, NSEC_PER_SEC,
+						    axpwm->rate),
+		.duty_length_ns =
+			mul_u64_u64_div_u64_roundup(duty, NSEC_PER_SEC,
+						    axpwm->rate),
+		.duty_offset_ns = 0,
+	};
+
+	return 0;
+}
+
+static int axiado_pwm_read_waveform(struct pwm_chip *chip,
+				    struct pwm_device *pwm,
+				    void *_wfhw)
+{
+	struct axiado_pwm_chip *axpwm = pwmchip_get_drvdata(chip);
+	struct axiado_pwm_waveform *wfhw = _wfhw;
+	u32 ctrl;
+
+	ctrl = readl(axpwm->base + AXIADO_PWM_CTRL_REG);
+
+	*wfhw = (struct axiado_pwm_waveform) {
+		.period = readl(axpwm->base + AXIADO_PWM_PERIOD_REG),
+		.duty = readl(axpwm->base + AXIADO_PWM_HIGH_REG),
+		.enabled = !!(ctrl & AXIADO_PWM_CTRL_ENABLE),
+	};
+
+	return 0;
+}
+
+static int axiado_pwm_write_waveform(struct pwm_chip *chip,
+				     struct pwm_device *pwm,
+				     const void *_wfhw)
+{
+	struct axiado_pwm_chip *axpwm = pwmchip_get_drvdata(chip);
+	const struct axiado_pwm_waveform *wfhw = _wfhw;
+
+	if (!wfhw->enabled) {
+		writel(0, axpwm->base + AXIADO_PWM_CTRL_REG);
+
+		return 0;
+	}
+
+	/*
+	 * The hardware has no shadow registers. These writes may alter the
+	 * active waveform before the current period has completed.
+	 */
+	writel(wfhw->period, axpwm->base + AXIADO_PWM_PERIOD_REG);
+	writel(wfhw->duty, axpwm->base + AXIADO_PWM_HIGH_REG);
+	writel(AXIADO_PWM_CTRL_ENABLE, axpwm->base + AXIADO_PWM_CTRL_REG);
+
+	return 0;
+}
+
+static const struct pwm_ops axiado_pwm_ops = {
+	.sizeof_wfhw = sizeof(struct axiado_pwm_waveform),
+	.round_waveform_tohw = axiado_pwm_round_waveform_tohw,
+	.round_waveform_fromhw = axiado_pwm_round_waveform_fromhw,
+	.read_waveform = axiado_pwm_read_waveform,
+	.write_waveform = axiado_pwm_write_waveform,
+};
+
+static int axiado_pwm_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct axiado_pwm_chip *axpwm;
+	struct pwm_chip *chip;
+	struct clk *clk;
+	int ret;
+
+	chip = devm_pwmchip_alloc(dev, 1, 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");
+
+	clk = devm_clk_get_enabled(dev, NULL);
+	if (IS_ERR(clk))
+		return dev_err_probe(dev, PTR_ERR(clk),
+				     "Failed to get/enable clock\n");
+
+	ret = devm_clk_rate_exclusive_get(dev, clk);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "Failed to lock clock rate\n");
+
+	axpwm->rate = clk_get_rate(clk);
+	if (!axpwm->rate)
+		return dev_err_probe(dev, -EINVAL,
+				     "Failed to get clock rate\n");
+
+	chip->ops = &axiado_pwm_ops;
+	chip->atomic = true;
+
+	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	[flat|nested] 5+ messages in thread

* Re: [PATCH v5 1/2] dt-bindings: pwm: add Axiado AX3000 PWM
  2026-09-21  7:21 ` [PATCH v5 1/2] dt-bindings: pwm: add Axiado AX3000 PWM Petar Stepanovic
@ 2026-09-21 14:35   ` Uwe Kleine-König
  0 siblings, 0 replies; 5+ messages in thread
From: Uwe Kleine-König @ 2026-09-21 14:35 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, openbmc, Conor Dooley

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

Hello Petar,

On Mon, Sep 21, 2026 at 12:21:30AM -0700, Petar Stepanovic wrote:
> +  "#pwm-cells":
> +    const: 2

Can you please make this 3?

Best regards
Uwe

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

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

* Re: [PATCH v5 2/2] pwm: add Axiado AX3000 PWM driver
  2026-09-21  7:21 ` [PATCH v5 2/2] pwm: add Axiado AX3000 PWM driver Petar Stepanovic
@ 2026-09-21 14:48   ` Uwe Kleine-König
  0 siblings, 0 replies; 5+ messages in thread
From: Uwe Kleine-König @ 2026-09-21 14:48 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, openbmc

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

On Mon, Sep 21, 2026 at 12:21:31AM -0700, Petar Stepanovic wrote:
> 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..781053e255f0
> --- /dev/null
> +++ b/drivers/pwm/pwm-axiado.c
> @@ -0,0 +1,277 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * SPDX-FileCopyrightText: 2021-2026 Axiado Corporation.
> + */
> +

Is there a public reference manual for that hardware? If so, adding a
link here would be awesome.

> +/*
> + * Limitations:
> + * - Only normal polarity is supported.
> + * - The hardware has no shadow registers, so configuration changes take
> + *   effect immediately without waiting for the current period to complete.
> + * - The output is driven high while the hardware is disabled.
> + * - Supported period range: 2 through 0xfffffffe PWM input clock cycles;
> + *   0xffffffff is reserved by the hardware for a constant low output.
> + *   Longer periods are clamped to the maximum.
> + * - A 0% duty cycle is emitted using the constant low encoding because the
> + *   hardware interprets a zero high time as a constant high output. The
> + *   requested period is lost in that case and is reported as a single clock
> + *   cycle.
> + * - A 100% duty cycle is supported and emits a constant high output.
> + */
> [...]
> +static int
> +axiado_pwm_round_waveform_tohw(struct pwm_chip *chip,
> +			       struct pwm_device *pwm,
> +			       const struct pwm_waveform *wf,
> +			       void *_wfhw)
> +{
> +	struct axiado_pwm_chip *axpwm = pwmchip_get_drvdata(chip);
> +	struct axiado_pwm_waveform *wfhw = _wfhw;
> +	u64 period;
> +	u64 duty;
> +	int ret = 0;
> +
> +	/* Encode a disabled request as a zeroed hardware waveform. */
> +	if (!wf->period_length_ns) {
> +		*wfhw = (struct axiado_pwm_waveform) { };
> +
> +		return 0;
> +	}
> +
> +	period = mul_u64_u64_div_u64(wf->period_length_ns, axpwm->rate,
> +				     NSEC_PER_SEC);
> +	duty = mul_u64_u64_div_u64(wf->duty_length_ns, axpwm->rate,
> +				   NSEC_PER_SEC);
> +
> +	if (!duty) {
> +		/*
> +		 * A zero high time makes the hardware emit a constant high
> +		 * output, so use the constant low period encoding for a 0%
> +		 * duty cycle. The high time must stay non-zero because it
> +		 * takes precedence over that encoding. The requested period
> +		 * is lost, .round_waveform_fromhw() reports a single clock
> +		 * cycle instead.
> +		 */
> +		*wfhw = (struct axiado_pwm_waveform) {
> +			.period = AXIADO_PWM_PERIOD_CONST_LOW,
> +			.duty = AXIADO_PWM_DUTY_MIN,
> +			.enabled = true,
> +		};
> +
> +		/* That single cycle is longer than a sub-cycle request. */
> +		return period ? 0 : 1;
> +	}
> +
> +	if (period < AXIADO_PWM_PERIOD_MIN) {
> +		period = AXIADO_PWM_PERIOD_MIN;
> +		ret = 1;
> +	} else if (period > AXIADO_PWM_PERIOD_MAX) {
> +		period = AXIADO_PWM_PERIOD_MAX;
> +	}
> +
> +	/*
> +	 * Period clamping can leave the converted duty greater than the
> +	 * final hardware period. In that case, clamp it to 100% duty.
> +	 */
> +	if (duty > period)
> +		duty = period;

So with period in [AXIADO_PWM_PERIOD_MIN, AXIADO_PWM_PERIOD_MAX], duty =
period yields a constant high output? If so, why is there a special
encoding for const high?

> +	*wfhw = (struct axiado_pwm_waveform) {
> +		.period = period,
> +		.duty = duty,
> +		.enabled = true,
> +	};
> +
> +	return ret;
> +}
> +
> +static int
> +axiado_pwm_round_waveform_fromhw(struct pwm_chip *chip,
> +				 struct pwm_device *pwm,
> +				 const void *_wfhw,
> +				 struct pwm_waveform *wf)
> +{
> +	struct axiado_pwm_chip *axpwm = pwmchip_get_drvdata(chip);
> +	const struct axiado_pwm_waveform *wfhw = _wfhw;
> +	u32 period = wfhw->period;
> +	u32 duty = wfhw->duty;
> +
> +	if (!wfhw->enabled) {
> +		*wf = (struct pwm_waveform) {
> +			.period_length_ns = 0,
> +		};
> +
> +		return 0;
> +	}
> +
> +	/*
> +	 * The constant low encoding doesn't hold a period. Report the
> +	 * shortest one, the output is low for all of it either way.
> +	 */
> +	if (period == AXIADO_PWM_PERIOD_CONST_LOW) {
> +		period = 1;
> +		duty = 0;
> +	} else if (duty > period) {
> +		duty = period;
> +	}

You don't handle wfhw->duty == 0 here. I think this is what Sashiko
pointed out. While .round_waveform_tohw() doens't produce this setting,
it would still be good to handle that in case the driver is loaded with
this setting applied in hardware.

> +	*wf = (struct pwm_waveform) {
> +		.period_length_ns =
> +			mul_u64_u64_div_u64_roundup(period, NSEC_PER_SEC,
> +						    axpwm->rate),
> +		.duty_length_ns =
> +			mul_u64_u64_div_u64_roundup(duty, NSEC_PER_SEC,
> +						    axpwm->rate),
> +		.duty_offset_ns = 0,
> +	};
> +
> +	return 0;
> +}

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-09-21 14:48 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-21  7:21 [PATCH v5 0/2] pwm: add Axiado AX3000 PWM support Petar Stepanovic
2026-09-21  7:21 ` [PATCH v5 1/2] dt-bindings: pwm: add Axiado AX3000 PWM Petar Stepanovic
2026-09-21 14:35   ` Uwe Kleine-König
2026-09-21  7:21 ` [PATCH v5 2/2] pwm: add Axiado AX3000 PWM driver Petar Stepanovic
2026-09-21 14:48   ` 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

all inboxes | Powered by JetHome®