The dwc pwm controller can be used in non-PCI systems, so allow
either platform or OF based probing.
Signed-off-by: Ben Dooks <ben.dooks@codethink.co.uk>
---
v9:
- add clk_rate_exclusive_get() to lock clock rate for now
- fix email address from sifive to codethink
- fix module namespace
v8:
- add compile test for of-case
- add module namespace
- move later in the series
v7:
- fixup kconfig from previous pcie changes
v5:
- fix missing " in kconfig
- remove .remove method, devm already sorts this.
- merge pwm-number code
- split the of code out of the core
- get bus clock
v4:
- moved the compile test code earlier
- fixed review comments
- used NS_PER_SEC
- use devm_clk_get_enabled
- ensure we get the bus clock
v3:
- changed compatible name
---
drivers/pwm/Kconfig | 10 ++++
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-dwc-core.c | 6 +++
drivers/pwm/pwm-dwc-of.c | 93 ++++++++++++++++++++++++++++++++++++++
drivers/pwm/pwm-dwc.c | 1 +
drivers/pwm/pwm-dwc.h | 1 +
6 files changed, 112 insertions(+)
create mode 100644 drivers/pwm/pwm-dwc-of.c
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 507c8b8547a5..22d58cb334e7 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -205,6 +205,16 @@ config PWM_DWC
To compile this driver as a module, choose M here: the module
will be called pwm-dwc.
+config PWM_DWC_OF
+ tristate "DesignWare PWM Controller (OF bus)"
+ depends on HAS_IOMEM && (OF || COMPILE_TEST)
+ select PWM_DWC_CORE
+ help
+ PWM driver for Synopsys DWC PWM Controller on an OF bus.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-dwc-of.
+
config PWM_EP93XX
tristate "Cirrus Logic EP93xx PWM support"
depends on ARCH_EP93XX || COMPILE_TEST
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index c5ec9e168ee7..e3c867cb13d1 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_PWM_CRC) += pwm-crc.o
obj-$(CONFIG_PWM_CROS_EC) += pwm-cros-ec.o
obj-$(CONFIG_PWM_DWC_CORE) += pwm-dwc-core.o
obj-$(CONFIG_PWM_DWC) += pwm-dwc.o
+obj-$(CONFIG_PWM_DWC_OF) += pwm-dwc-of.o
obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o
obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o
obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o
diff --git a/drivers/pwm/pwm-dwc-core.c b/drivers/pwm/pwm-dwc-core.c
index 6358e3345210..8b4d96d655c7 100644
--- a/drivers/pwm/pwm-dwc-core.c
+++ b/drivers/pwm/pwm-dwc-core.c
@@ -16,6 +16,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <linux/pwm.h>
@@ -44,6 +45,9 @@ static int __dwc_pwm_configure_timer(struct dwc_pwm *dwc,
u32 high;
u32 low;
+ if (dwc->clk)
+ dwc->clk_rate = clk_get_rate(dwc->clk);
+
/*
* Calculate width of low and high period in terms of input clock
* periods and check are the result within HW limits between 1 and
@@ -128,6 +132,8 @@ static int dwc_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
pm_runtime_get_sync(chip->dev);
+ if (dwc->clk)
+ dwc->clk_rate = clk_get_rate(dwc->clk);
clk_rate = dwc->clk_rate;
ctrl = dwc_pwm_readl(dwc, DWC_TIM_CTRL(pwm->hwpwm));
diff --git a/drivers/pwm/pwm-dwc-of.c b/drivers/pwm/pwm-dwc-of.c
new file mode 100644
index 000000000000..30b860484895
--- /dev/null
+++ b/drivers/pwm/pwm-dwc-of.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DesignWare PWM Controller driver OF
+ *
+ * Copyright (C) 2022 SiFive, Inc.
+ */
+
+#define DEFAULT_MODULE_NAMESPACE dwc_pwm
+
+#include <linux/bitops.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/pwm.h>
+#include <linux/io.h>
+
+#include "pwm-dwc.h"
+
+static int dwc_pwm_plat_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct dwc_pwm *dwc;
+ struct clk *bus;
+ u32 nr_pwm;
+ int ret;
+
+ dwc = dwc_pwm_alloc(dev);
+ if (!dwc)
+ return -ENOMEM;
+
+ if (!device_property_read_u32(dev, "snps,pwm-number", &nr_pwm)) {
+ if (nr_pwm > DWC_TIMERS_TOTAL)
+ dev_err(dev, "too many PWMs (%d) specified, capping at %d\n",
+ nr_pwm, dwc->chip.npwm);
+ else
+ dwc->chip.npwm = nr_pwm;
+ }
+
+ dwc->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(dwc->base))
+ return PTR_ERR(dwc->base);
+
+ bus = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(bus))
+ return dev_err_probe(dev, PTR_ERR(bus),
+ "failed to get clock\n");
+
+ dwc->clk = devm_clk_get_enabled(dev, "timer");
+ if (IS_ERR(dwc->clk))
+ return dev_err_probe(dev, PTR_ERR(dwc->clk),
+ "failed to get timer clock\n");
+
+ ret = clk_rate_exclusive_get(dwc->clk);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "clk_rate_exclusive_get() failed\n");
+
+ dwc->clk_rate = clk_get_rate(dwc->clk);
+ return devm_pwmchip_add(dev, &dwc->chip);
+}
+
+static int dwc_pwm_plat_remove(struct platform_device *pdev)
+{
+ struct dwc_pwm *dwc = dev_get_drvdata(&pdev->dev);
+
+ clk_rate_exclusive_put(dwc->clk);
+ return 0;
+}
+
+static const struct of_device_id dwc_pwm_dt_ids[] = {
+ { .compatible = "snps,dw-apb-timers-pwm2" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, dwc_pwm_dt_ids);
+
+static struct platform_driver dwc_pwm_plat_driver = {
+ .driver = {
+ .name = "dwc-pwm",
+ .of_match_table = dwc_pwm_dt_ids,
+ },
+ .probe = dwc_pwm_plat_probe,
+ .remove = dwc_pwm_plat_remove,
+};
+
+module_platform_driver(dwc_pwm_plat_driver);
+
+MODULE_ALIAS("platform:dwc-pwm-of");
+MODULE_AUTHOR("Ben Dooks <ben.dooks@codethink.co.uk>");
+MODULE_DESCRIPTION("DesignWare PWM Controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-dwc.c b/drivers/pwm/pwm-dwc.c
index bd9cadb497d7..7c32bd06ed33 100644
--- a/drivers/pwm/pwm-dwc.c
+++ b/drivers/pwm/pwm-dwc.c
@@ -20,6 +20,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <linux/pwm.h>
diff --git a/drivers/pwm/pwm-dwc.h b/drivers/pwm/pwm-dwc.h
index e0a940fd6e87..18e98c2c07d7 100644
--- a/drivers/pwm/pwm-dwc.h
+++ b/drivers/pwm/pwm-dwc.h
@@ -42,6 +42,7 @@ struct dwc_pwm_ctx {
struct dwc_pwm {
struct pwm_chip chip;
void __iomem *base;
+ struct clk *clk;
unsigned long clk_rate;
struct dwc_pwm_ctx ctx[DWC_TIMERS_TOTAL];
};
--
2.40.1
> The dwc pwm controller can be used in non-PCI systems, so allow > either platform or OF based probing. > Hi Ben, We're currently working on a platform driver for the DW_apb_timers PWM controller used in our EIC7700 SoC. We noticed that you submitted a patch for a DW PWM platform controller back in 2023, and we would like to kindly ask about its current status. Do you have any plans to get it merged into mainline? Regards, Xuyang Dong
On 10/11/2025 09:05, dongxuyang@eswincomputing.com wrote: >> The dwc pwm controller can be used in non-PCI systems, so allow >> either platform or OF based probing. >> > > Hi Ben, > > We're currently working on a platform driver for the DW_apb_timers PWM > controller used in our EIC7700 SoC. We noticed that you submitted a patch > for a DW PWM platform controller back in 2023, and we would like to kindly > ask about its current status. Do you have any plans to get it merged > into mainline? Hi, I think we got a lot of the code re-ordering in but never finished as the project came to an end and our customer decided never to ship any actual silicon. I am not sure how well our patches will apply now, but could have a quick look at this to see how much is left to do. Unfortunately we had to delete everything at the end of the project so I do not have any sort of test setup. -- Ben Dooks http://www.codethink.co.uk/ Senior Engineer Codethink - Providing Genius https://www.codethink.co.uk/privacy.html
© 2016 - 2025 Red Hat, Inc.