Skip to contentSkip to conversation

Add support for nuvoton ma35d1 pwm controller

PATCH In review Chi-Wen Weng 3 participants 6 messages Dec 6, 2024 – Feb 12, 2025

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

v3 cover Add support for nuvoton ma35d1 pwm controller

This patch series adds pwm driver for the nuvoton ma35d1 ARMv8 SoC. It includes DT binding documentation and the ma35d1 pwm driver.

v3:
- Update nuvoton,ma35d1-pwm.yaml
- Add maintainers entry
- Increse "#pwm-cells" to 3
- Update ma35d1 pwm driver
- Make include header and macros definitions organized alphabetically
- Rename macros REG_PWM_XXXX to MA35D1_REG_PWM_XXXX
- Add macros for register address

v2 resend:
- Remove wrong 'Reviewed-by' tags.

v2:
- Update nuvoton,ma35d1-pwm.yaml
- Fix 'maxItems' of 'reg' to 1.
- Remove unused label
- Update ma35d1 pwm driver
- Remove MODULE_ALIAS()
- Add chip->atomic = true

Chi-Wen Weng (2):
Add dt-bindings for Nuvoton MA35D1 SoC PWM controller.
pwm: Add Nuvoton MA35D1 PWM controller support

 .../bindings/pwm/nuvoton,ma35d1-pwm.yaml      |  45 +++++
 drivers/pwm/Kconfig                           |   9 +
 drivers/pwm/Makefile                          |   1 +
 drivers/pwm/pwm-ma35d1.c                      | 179 ++++++++++++++++++
 4 files changed, 234 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pwm/nuvoton,ma35d1-pwm.yaml
 create mode 100644 drivers/pwm/pwm-ma35d1.c
Signature · 1 line
2.25.1
#1 lore raw

PATCH v3 1/2 dt-bindings: pwm: nuvoton: Add MA35D1 pwm

Add dt-bindings for Nuvoton MA35D1 SoC PWM controller.

---
 .../bindings/pwm/nuvoton,ma35d1-pwm.yaml      | 45 +++++++++++++++++++
 1 file changed, 45 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pwm/nuvoton,ma35d1-pwm.yaml
Documentation/devicetree/bindings/pwm/nuvoton,ma35d1-pwm.yaml new file +45 -0
new file mode 100644
index 000000000000..47a59bdd14d0
@@ -0,0 +1,45 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pwm/nuvoton,ma35d1-pwm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nuvoton MA35D1 PWM controller
+
+maintainers:
+  - Chi-Wen Weng <[email protected]>
+
+allOf:
+  - $ref: pwm.yaml#
+
+properties:
+  compatible:
+    enum:
+      - nuvoton,ma35d1-pwm
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  "#pwm-cells":
+    const: 3
+
+required:
+  - compatible
+  - reg
+  - clocks
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
+
+    pwm@40580000 {
+      compatible = "nuvoton,ma35d1-pwm";
+      reg = <0x40580000 0x400>;
+      clocks = <&clk EPWM0_GATE>;
+      #pwm-cells = <3>;
+    };
Signature · 1 line
2.25.1
#2 lore raw

PATCH v3 2/2 pwm: Add Nuvoton MA35D1 PWM controller support

In reply to Chi-Wen Weng

This commit adds a generic PWM framework driver for Nuvoton MA35D1 PWM controller.

---
 drivers/pwm/Kconfig      |   9 ++
 drivers/pwm/Makefile     |   1 +
 drivers/pwm/pwm-ma35d1.c | 179 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 189 insertions(+)
 create mode 100644 drivers/pwm/pwm-ma35d1.c
drivers/pwm/Kconfig +9 -0
index 0915c1e7df16..97b9e83af020 100644
@@ -411,6 +411,15 @@ config PWM_LPSS_PLATFORM
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-lpss-platform.
 
+config PWM_MA35D1
+	tristate "Nuvoton MA35D1 PWM support"
+	depends on ARCH_MA35 || COMPILE_TEST
+	help
+	  Generic PWM framework driver for Nuvoton MA35D1.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-ma35d1.
+
 config PWM_MESON
 	tristate "Amlogic Meson PWM driver"
 	depends on ARCH_MESON || COMPILE_TEST
drivers/pwm/Makefile +1 -0
index 9081e0c0e9e0..c1d3a1d8add0 100644
@@ -36,6 +36,7 @@ obj-$(CONFIG_PWM_LPC32XX)	+= pwm-lpc32xx.o
 obj-$(CONFIG_PWM_LPSS)		+= pwm-lpss.o
 obj-$(CONFIG_PWM_LPSS_PCI)	+= pwm-lpss-pci.o
 obj-$(CONFIG_PWM_LPSS_PLATFORM)	+= pwm-lpss-platform.o
+obj-$(CONFIG_PWM_MA35D1)	+= pwm-ma35d1.o
 obj-$(CONFIG_PWM_MESON)		+= pwm-meson.o
 obj-$(CONFIG_PWM_MEDIATEK)	+= pwm-mediatek.o
 obj-$(CONFIG_PWM_MICROCHIP_CORE)	+= pwm-microchip-core.o
drivers/pwm/pwm-ma35d1.c new file +179 -0

179 lines not on this page · raw message

Signature · 1 line
2.25.1
#4 lore raw

Re: [PATCH v3 2/2] pwm: Add Nuvoton MA35D1 PWM controller support

In reply to Chi-Wen Weng

Hello,

drivers/pwm/pwm-ma35d1.c line 6

Quoted 13 lines · Chi-Wen Weng

[...]

drivers/pwm/pwm-ma35d1.c new file +6 -0
new file mode 100644
index 000000000000..380f17b20a3d
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the Nuvoton MA35D1 PWM controller
+ *
+ * Copyright (C) 2024 Nuvoton Corporation
+ *               Chi-Wen Weng <[email protected]>

Please add some information here about the hardware. The idea is to get some info about the device's capabilities. Please stick to the format that many other drivers are using such that

	sed -rn '/Limitations:/,/\*\/?$/p' drivers/pwm/pwm-ma35d1.c

emits the info for your driver. Things to mention are: possible glitches during .apply(); behaviour of the pin when the PWM is disabled (constant signal? High-Z?)

Also a link to a reference manual would be awesome.

Quoted 18 lines
drivers/pwm/pwm-ma35d1.c new file +18 -0
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/math64.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+/* The following are registers address offset for MA35D1 PWM controller */
+#define MA35D1_REG_PWM_CTL0            (0x00)
+#define MA35D1_REG_PWM_CNTEN           (0x20)
+#define MA35D1_REG_PWM_PERIOD0         (0x30)
+#define MA35D1_REG_PWM_CMPDAT0         (0x50)
+#define MA35D1_REG_PWM_WGCTL0          (0xB0)
+#define MA35D1_REG_PWM_POLCTL          (0xD4)
+#define MA35D1_REG_PWM_POEN            (0xD8)

drivers/pwm/pwm-ma35d1.c line 34

Quoted 10 lines
drivers/pwm/pwm-ma35d1.c new file +10 -0
+
+/* The following are register address of MA35D1 PWM controller */
+#define MA35D1_PWM_CH_REG_SIZE         4
+#define MA35D1_PWM_CMPDAT0_ADDR(base, ch)     ((base) + MA35D1_REG_PWM_CMPDAT0 + \
+					       ((ch) * MA35D1_PWM_CH_REG_SIZE))
+#define MA35D1_PWM_CNTEN_ADDR(base)           ((base) + MA35D1_REG_PWM_CNTEN)
+#define MA35D1_PWM_PERIOD0_ADDR(base, ch)     ((base) + MA35D1_REG_PWM_PERIOD0 + \
+					       ((ch) * MA35D1_PWM_CH_REG_SIZE))
+#define MA35D1_PWM_POEN_ADDR(base)            ((base) + MA35D1_REG_PWM_POEN)
+#define MA35D1_PWM_POLCTL_ADDR(base)          ((base) + MA35D1_REG_PWM_POLCTL)

I would drop the base part in these defines and add them to the above list sorted by address.

So something like:

#define MA35D1_REG_PWM_CTL0 0x00
#define MA35D1_REG_PWM_CNTEN 0x20
#define MA35D1_REG_PWM_PERIOD(ch) (0x30 + 4 * (ch))
#define MA35D1_REG_PWM_CMPDAT(ch) (0x50 + 4 * (ch))
#define MA35D1_REG_PWM_WGCTL0 0xB0
#define MA35D1_REG_PWM_POLCTL 0xD4
#define MA35D1_REG_PWM_POEN 0xD8

To make up for the missing base, I'd create wrappers for readl and writel à la:

	static u32 nuvoton_pwm_readl(struct nuvoton_pwm *nvtpwm, unsigned int offset);
	static void nuvoton_pwm_writel(struct nuvoton_pwm *nvtpwm, unsigned int offset, u32 value);

As you see I wouldn't use a define for 4, because IMHO that hurts readability. But I don't feel strong here.

drivers/pwm/pwm-ma35d1.c line 37

Quoted 2 lines
drivers/pwm/pwm-ma35d1.c new file +2 -0
+#define MA35D1_PWM_MAX_COUNT           0xFFFF
+#define MA35D1_PWM_TOTAL_CHANNELS      6

s/TOTAL/NUM/

drivers/pwm/pwm-ma35d1.c line 41

Quoted 4 lines
drivers/pwm/pwm-ma35d1.c new file +4 -0
+
+struct nuvoton_pwm {
+	void __iomem *base;
+	u64 clkrate;

clk_get_rate() returns an unsigned long value. Please stick to that for .clkrate as there is no use to double the size of this struct on 32-bit platforms just to store zeros and padding.

drivers/pwm/pwm-ma35d1.c line 47

Quoted 6 lines
drivers/pwm/pwm-ma35d1.c new file +6 -0
+};
+
+static inline struct nuvoton_pwm *to_nuvoton_pwm(struct pwm_chip *chip)
+{
+	return pwmchip_get_drvdata(chip);
+}

I slightly prefer nuvoton_pwm_from_chip() for this function's name to have the same function prefix for all functions here.

Quoted 21 lines
drivers/pwm/pwm-ma35d1.c new file +21 -0
+
+static int nuvoton_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+			     const struct pwm_state *state)
+{
+	struct nuvoton_pwm *nvtpwm;
+	u32 ch = pwm->hwpwm;
+
+	nvtpwm = to_nuvoton_pwm(chip);
+	if (state->enabled) {
+		u64 duty_cycles, period_cycles;
+
+		/* Calculate the duty and period cycles */
+		duty_cycles = mul_u64_u64_div_u64(nvtpwm->clkrate,
+						  state->duty_cycle, NSEC_PER_SEC);
+		if (duty_cycles > MA35D1_PWM_MAX_COUNT)
+			duty_cycles = MA35D1_PWM_MAX_COUNT;
+
+		period_cycles = mul_u64_u64_div_u64(nvtpwm->clkrate,
+						    state->period, NSEC_PER_SEC);
+		if (period_cycles > MA35D1_PWM_MAX_COUNT)
+			period_cycles = MA35D1_PWM_MAX_COUNT;

drivers/pwm/pwm-ma35d1.c line 78

Quoted 10 lines
drivers/pwm/pwm-ma35d1.c new file +10 -0
+
+		/* Write the duty and period cycles to registers */
+		writel(duty_cycles, MA35D1_PWM_CMPDAT0_ADDR(nvtpwm->base, ch));
+		writel(period_cycles, MA35D1_PWM_PERIOD0_ADDR(nvtpwm->base, ch));
+		/* Enable counter */
+		writel(readl(MA35D1_PWM_CNTEN_ADDR(nvtpwm->base)) | BIT(ch),
+		       MA35D1_PWM_CNTEN_ADDR(nvtpwm->base));
+		/* Enable output */
+		writel(readl(MA35D1_PWM_POEN_ADDR(nvtpwm->base)) | BIT(ch),
+		       MA35D1_PWM_POEN_ADDR(nvtpwm->base));

Can this result in glitches? E.g. is it possible to see a waveform with the old period_cycles but the new duty_cycles output? If you switch polarity, do you see the new settings with the wrong polarity for some time? Setup polarity before enabling the counter and output? Maybe only enable the counter when the output is enabled to be sure to emit the first edge with the correct timing?

drivers/pwm/pwm-ma35d1.c line 85

Quoted 7 lines
drivers/pwm/pwm-ma35d1.c new file +7 -0
+	} else {
+		/* Disable counter */
+		writel(readl(MA35D1_PWM_CNTEN_ADDR(nvtpwm->base)) & ~BIT(ch),
+		       MA35D1_PWM_CNTEN_ADDR(nvtpwm->base));
+		/* Disable output */
+		writel(readl(MA35D1_PWM_POEN_ADDR(nvtpwm->base)) & ~BIT(ch),
+		       MA35D1_PWM_POEN_ADDR(nvtpwm->base));

Maybe add another wrapper for this kind of rmw operation. Also I suggest to introduce a name for BIT(ch) here such that this can become:

	nuvoton_pwm_rmw(nvtpwm, MA35D1_REG_PWM_POEN, MA35D1_REG_PWM_POEN_EN(ch), 0);

(Assuming "EN0" is the name of the bit for channel 0 in MA35D1_REG_PWM_POEN.)

drivers/pwm/pwm-ma35d1.c line 94

Quoted 9 lines
drivers/pwm/pwm-ma35d1.c new file +9 -0
+	}
+
+	/* Set polarity state to register */
+	if (state->polarity == PWM_POLARITY_NORMAL)
+		writel(readl(MA35D1_PWM_POLCTL_ADDR(nvtpwm->base)) & ~BIT(ch),
+		       MA35D1_PWM_POLCTL_ADDR(nvtpwm->base));
+	else
+		writel(readl(MA35D1_PWM_POLCTL_ADDR(nvtpwm->base)) | BIT(ch),
+		       MA35D1_PWM_POLCTL_ADDR(nvtpwm->base));

You can skip setting up period if !state->enabled.

Quoted 15 lines
drivers/pwm/pwm-ma35d1.c new file +14 -0
+	return 0;
+}
+
[...]
+
+static int nuvoton_pwm_probe(struct platform_device *pdev)
+{
+	struct pwm_chip *chip;
+	struct nuvoton_pwm *nvtpwm;
+	struct clk *clk;
+	int ret;
+
+	chip = devm_pwmchip_alloc(&pdev->dev, MA35D1_PWM_TOTAL_CHANNELS, sizeof(*nvtpwm));
+	if (IS_ERR(chip))
+		return PTR_ERR(chip);

drivers/pwm/pwm-ma35d1.c line 146

Quoted 10 lines
drivers/pwm/pwm-ma35d1.c new file +10 -0
+
+	nvtpwm = to_nuvoton_pwm(chip);
+
+	nvtpwm->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(nvtpwm->base))
+		return PTR_ERR(nvtpwm->base);
+
+	clk = devm_clk_get_enabled(&pdev->dev, NULL);
+	if (IS_ERR(clk))
+		return dev_err_probe(&pdev->dev, PTR_ERR(clk), "unable to get the clock");

Please start all error messages with a capital letter and end them with \n.

devm_clk_rate_exclusive_get(&pdev->dev, clk) here please.

drivers/pwm/pwm-ma35d1.c line 150

Quoted 4 lines
drivers/pwm/pwm-ma35d1.c new file +4 -0
+
+	nvtpwm->clkrate = clk_get_rate(clk);
+	if (nvtpwm->clkrate > NSEC_PER_SEC)
+		return dev_err_probe(&pdev->dev, -EINVAL, "pwm clock out of range");
	return dev_err_probe(&pdev->dev, -EINVAL, "PWM clock out of range (%lu)\n", nvtpwm->clkrate);

drivers/pwm/pwm-ma35d1.c line 160

Quoted 10 lines
drivers/pwm/pwm-ma35d1.c new file +10 -0
+
+	chip->ops = &nuvoton_pwm_ops;
+	chip->atomic = true;
+
+	ret = devm_pwmchip_add(&pdev->dev, chip);
+	if (ret < 0)
+		return dev_err_probe(&pdev->dev, ret, "unable to add pwm chip");
+
+	return 0;
+}

Best regards
Uwe

#5 lore raw

Re: [PATCH v3 2/2] pwm: Add Nuvoton MA35D1 PWM controller support

Hi Uwe,

Thanks for your reply.

Quoted 20 lines · Uwe Kleine-König

Hello,

drivers/pwm/pwm-ma35d1.c line 6

Quoted 13 lines · Chi-Wen Weng

[...]

drivers/pwm/pwm-ma35d1.c new file +6 -0
new file mode 100644
index 000000000000..380f17b20a3d
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the Nuvoton MA35D1 PWM controller
+ *
+ * Copyright (C) 2024 Nuvoton Corporation
+ *               Chi-Wen Weng <[email protected]>

Please add some information here about the hardware. The idea is to get some info about the device's capabilities. Please stick to the format that many other drivers are using such that

Quoted 7 lines
	sed -rn '/Limitations:/,/\*\/?$/p' drivers/pwm/pwm-ma35d1.c

emits the info for your driver. Things to mention are: possible glitches during .apply(); behaviour of the pin when the PWM is disabled (constant signal? High-Z?)

Also a link to a reference manual would be awesome.

Okay, I will add the information into driver.

Quoted 41 lines
Quoted 18 lines
drivers/pwm/pwm-ma35d1.c new file +18 -0
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/math64.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+/* The following are registers address offset for MA35D1 PWM controller */
+#define MA35D1_REG_PWM_CTL0            (0x00)
+#define MA35D1_REG_PWM_CNTEN           (0x20)
+#define MA35D1_REG_PWM_PERIOD0         (0x30)
+#define MA35D1_REG_PWM_CMPDAT0         (0x50)
+#define MA35D1_REG_PWM_WGCTL0          (0xB0)
+#define MA35D1_REG_PWM_POLCTL          (0xD4)
+#define MA35D1_REG_PWM_POEN            (0xD8)

drivers/pwm/pwm-ma35d1.c line 34

Quoted 10 lines
drivers/pwm/pwm-ma35d1.c new file +10 -0
+
+/* The following are register address of MA35D1 PWM controller */
+#define MA35D1_PWM_CH_REG_SIZE         4
+#define MA35D1_PWM_CMPDAT0_ADDR(base, ch)     ((base) + MA35D1_REG_PWM_CMPDAT0 + \
+					       ((ch) * MA35D1_PWM_CH_REG_SIZE))
+#define MA35D1_PWM_CNTEN_ADDR(base)           ((base) + MA35D1_REG_PWM_CNTEN)
+#define MA35D1_PWM_PERIOD0_ADDR(base, ch)     ((base) + MA35D1_REG_PWM_PERIOD0 + \
+					       ((ch) * MA35D1_PWM_CH_REG_SIZE))
+#define MA35D1_PWM_POEN_ADDR(base)            ((base) + MA35D1_REG_PWM_POEN)
+#define MA35D1_PWM_POLCTL_ADDR(base)          ((base) + MA35D1_REG_PWM_POLCTL)

I would drop the base part in these defines and add them to the above list sorted by address.

So something like:

#define MA35D1_REG_PWM_CTL0 0x00
#define MA35D1_REG_PWM_CNTEN 0x20
#define MA35D1_REG_PWM_PERIOD(ch) (0x30 + 4 * (ch))
#define MA35D1_REG_PWM_CMPDAT(ch) (0x50 + 4 * (ch))
#define MA35D1_REG_PWM_WGCTL0 0xB0
#define MA35D1_REG_PWM_POLCTL 0xD4
#define MA35D1_REG_PWM_POEN 0xD8

Quoted 8 lines

To make up for the missing base, I'd create wrappers for readl and writel à la:

	static u32 nuvoton_pwm_readl(struct nuvoton_pwm *nvtpwm, unsigned int offset);
	static void nuvoton_pwm_writel(struct nuvoton_pwm *nvtpwm, unsigned int offset, u32 value);

As you see I wouldn't use a define for 4, because IMHO that hurts readability. But I don't feel strong here.

It really can let my driver more readable. Thanks.

Quoted 4 lines

drivers/pwm/pwm-ma35d1.c line 37

Quoted 3 lines
drivers/pwm/pwm-ma35d1.c new file +2 -0
+#define MA35D1_PWM_MAX_COUNT           0xFFFF
+#define MA35D1_PWM_TOTAL_CHANNELS      6

s/TOTAL/NUM/

Okay, I will modify it in next version.

Quoted 7 lines

drivers/pwm/pwm-ma35d1.c line 41

Quoted 4 lines
drivers/pwm/pwm-ma35d1.c new file +4 -0
+
+struct nuvoton_pwm {
+	void __iomem *base;
+	u64 clkrate;

clk_get_rate() returns an unsigned long value. Please stick to that for .clkrate as there is no use to double the size of this struct on 32-bit platforms just to store zeros and padding.

Okay, I will modify it in next version.

Quoted 8 lines

drivers/pwm/pwm-ma35d1.c line 47

Quoted 6 lines
drivers/pwm/pwm-ma35d1.c new file +6 -0
+};
+
+static inline struct nuvoton_pwm *to_nuvoton_pwm(struct pwm_chip *chip)
+{
+	return pwmchip_get_drvdata(chip);
+}

I slightly prefer nuvoton_pwm_from_chip() for this function's name to have the same function prefix for all functions here.

Okay, I will modify it in next version.

Quoted 29 lines
Quoted 29 lines
drivers/pwm/pwm-ma35d1.c new file +29 -0
+
+static int nuvoton_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+			     const struct pwm_state *state)
+{
+	struct nuvoton_pwm *nvtpwm;
+	u32 ch = pwm->hwpwm;
+
+	nvtpwm = to_nuvoton_pwm(chip);
+	if (state->enabled) {
+		u64 duty_cycles, period_cycles;
+
+		/* Calculate the duty and period cycles */
+		duty_cycles = mul_u64_u64_div_u64(nvtpwm->clkrate,
+						  state->duty_cycle, NSEC_PER_SEC);
+		if (duty_cycles > MA35D1_PWM_MAX_COUNT)
+			duty_cycles = MA35D1_PWM_MAX_COUNT;
+
+		period_cycles = mul_u64_u64_div_u64(nvtpwm->clkrate,
+						    state->period, NSEC_PER_SEC);
+		if (period_cycles > MA35D1_PWM_MAX_COUNT)
+			period_cycles = MA35D1_PWM_MAX_COUNT;
+
+		/* Write the duty and period cycles to registers */
+		writel(duty_cycles, MA35D1_PWM_CMPDAT0_ADDR(nvtpwm->base, ch));
+		writel(period_cycles, MA35D1_PWM_PERIOD0_ADDR(nvtpwm->base, ch));
+		/* Enable counter */
+		writel(readl(MA35D1_PWM_CNTEN_ADDR(nvtpwm->base)) | BIT(ch),
+		       MA35D1_PWM_CNTEN_ADDR(nvtpwm->base));
+		/* Enable output */
Quoted 8 lines

drivers/pwm/pwm-ma35d1.c line 77

Quoted 2 lines
drivers/pwm/pwm-ma35d1.c new file +2 -0
+		writel(readl(MA35D1_PWM_POEN_ADDR(nvtpwm->base)) | BIT(ch),
+		       MA35D1_PWM_POEN_ADDR(nvtpwm->base));

Can this result in glitches? E.g. is it possible to see a waveform with the old period_cycles but the new duty_cycles output? If you switch polarity, do you see the new settings with the wrong polarity for some time? Setup polarity before enabling the counter and output? Maybe only enable the counter when the output is enabled to be sure to emit the first edge with the correct timing?

Yes, enable counter should be after the output is enabled. Will fix in next version.

Quoted 10 lines

drivers/pwm/pwm-ma35d1.c line 85

Quoted 7 lines
drivers/pwm/pwm-ma35d1.c new file +7 -0
+	} else {
+		/* Disable counter */
+		writel(readl(MA35D1_PWM_CNTEN_ADDR(nvtpwm->base)) & ~BIT(ch),
+		       MA35D1_PWM_CNTEN_ADDR(nvtpwm->base));
+		/* Disable output */
+		writel(readl(MA35D1_PWM_POEN_ADDR(nvtpwm->base)) & ~BIT(ch),
+		       MA35D1_PWM_POEN_ADDR(nvtpwm->base));

Maybe add another wrapper for this kind of rmw operation. Also I suggest to introduce a name for BIT(ch) here such that this can become:

Quoted 4 lines
	nuvoton_pwm_rmw(nvtpwm, MA35D1_REG_PWM_POEN, MA35D1_REG_PWM_POEN_EN(ch), 0);

(Assuming "EN0" is the name of the bit for channel 0 in MA35D1_REG_PWM_POEN.)

Okay, I will modify it next version.

Quoted 10 lines

drivers/pwm/pwm-ma35d1.c line 94

Quoted 9 lines
drivers/pwm/pwm-ma35d1.c new file +9 -0
+	}
+
+	/* Set polarity state to register */
+	if (state->polarity == PWM_POLARITY_NORMAL)
+		writel(readl(MA35D1_PWM_POLCTL_ADDR(nvtpwm->base)) & ~BIT(ch),
+		       MA35D1_PWM_POLCTL_ADDR(nvtpwm->base));
+	else
+		writel(readl(MA35D1_PWM_POLCTL_ADDR(nvtpwm->base)) | BIT(ch),
+		       MA35D1_PWM_POLCTL_ADDR(nvtpwm->base));

You can skip setting up period if !state->enabled.

Yes, it should be skip if !state->enabled.

Quoted 28 lines
Quoted 15 lines
drivers/pwm/pwm-ma35d1.c new file +14 -0
+	return 0;
+}
+
[...]
+
+static int nuvoton_pwm_probe(struct platform_device *pdev)
+{
+	struct pwm_chip *chip;
+	struct nuvoton_pwm *nvtpwm;
+	struct clk *clk;
+	int ret;
+
+	chip = devm_pwmchip_alloc(&pdev->dev, MA35D1_PWM_TOTAL_CHANNELS, sizeof(*nvtpwm));
+	if (IS_ERR(chip))
+		return PTR_ERR(chip);

drivers/pwm/pwm-ma35d1.c line 146

Quoted 10 lines
drivers/pwm/pwm-ma35d1.c new file +10 -0
+
+	nvtpwm = to_nuvoton_pwm(chip);
+
+	nvtpwm->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(nvtpwm->base))
+		return PTR_ERR(nvtpwm->base);
+
+	clk = devm_clk_get_enabled(&pdev->dev, NULL);
+	if (IS_ERR(clk))
+		return dev_err_probe(&pdev->dev, PTR_ERR(clk), "unable to get the clock");

Please start all error messages with a capital letter and end them with \n.

Quoted 1 line

devm_clk_rate_exclusive_get(&pdev->dev, clk) here please.

Okay, I will modify it in next version.

Quoted 5 lines

drivers/pwm/pwm-ma35d1.c line 150

Quoted 4 lines
drivers/pwm/pwm-ma35d1.c new file +4 -0
+
+	nvtpwm->clkrate = clk_get_rate(clk);
+	if (nvtpwm->clkrate > NSEC_PER_SEC)
+		return dev_err_probe(&pdev->dev, -EINVAL, "pwm clock out of range");
	return dev_err_probe(&pdev->dev, -EINVAL, "PWM clock out of range (%lu)\n", nvtpwm->clkrate);

Okay, I will modify it in next version.

Quoted 4 lines
Quoted 4 lines
drivers/pwm/pwm-ma35d1.c new file +4 -0
+
+	chip->ops = &nuvoton_pwm_ops;
+	chip->atomic = true;
+
Quoted 8 lines

drivers/pwm/pwm-ma35d1.c line 160

Quoted 6 lines
drivers/pwm/pwm-ma35d1.c new file +6 -0
+	ret = devm_pwmchip_add(&pdev->dev, chip);
+	if (ret < 0)
+		return dev_err_probe(&pdev->dev, ret, "unable to add pwm chip");
+
+	return 0;
+}

Best regards
Uwe

Thanks.

Chi-Wen Weng