{"id":2218987,"url":"http://patchwork.ozlabs.org/api/patches/2218987/?format=json","web_url":"http://patchwork.ozlabs.org/project/linux-pwm/patch/20260402091920.1721-1-dongxuyang@eswincomputing.com/","project":{"id":38,"url":"http://patchwork.ozlabs.org/api/projects/38/?format=json","name":"Linux PWM development","link_name":"linux-pwm","list_id":"linux-pwm.vger.kernel.org","list_email":"linux-pwm@vger.kernel.org","web_url":"","scm_url":"","webscm_url":"","list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<20260402091920.1721-1-dongxuyang@eswincomputing.com>","list_archive_url":null,"date":"2026-04-02T09:19:20","name":"[v3,2/2] pwm: dwc: add of/platform support","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"49ce5f389734c28000f816103f96a26125097da4","submitter":{"id":90849,"url":"http://patchwork.ozlabs.org/api/people/90849/?format=json","name":"Xuyang Dong","email":"dongxuyang@eswincomputing.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/linux-pwm/patch/20260402091920.1721-1-dongxuyang@eswincomputing.com/mbox/","series":[{"id":498455,"url":"http://patchwork.ozlabs.org/api/series/498455/?format=json","web_url":"http://patchwork.ozlabs.org/project/linux-pwm/list/?series=498455","date":"2026-04-02T09:18:54","name":"Update designware pwm driver","version":3,"mbox":"http://patchwork.ozlabs.org/series/498455/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2218987/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2218987/checks/","tags":{},"related":[],"headers":{"Return-Path":"\n <linux-pwm+bounces-8456-incoming=patchwork.ozlabs.org@vger.kernel.org>","X-Original-To":["incoming@patchwork.ozlabs.org","linux-pwm@vger.kernel.org"],"Delivered-To":"patchwork-incoming@legolas.ozlabs.org","Authentication-Results":["legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=2600:3c09:e001:a7::12fc:5321; helo=sto.lore.kernel.org;\n envelope-from=linux-pwm+bounces-8456-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)","smtp.subspace.kernel.org;\n arc=none smtp.client-ip=162.243.161.220","smtp.subspace.kernel.org;\n dmarc=none (p=none dis=none) header.from=eswincomputing.com","smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=eswincomputing.com"],"Received":["from sto.lore.kernel.org (sto.lore.kernel.org\n [IPv6:2600:3c09:e001:a7::12fc:5321])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fmbz10ggSz1yGH\n\tfor <incoming@patchwork.ozlabs.org>; Thu, 02 Apr 2026 20:24:29 +1100 (AEDT)","from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sto.lore.kernel.org (Postfix) with ESMTP id 3DD57307C232\n\tfor <incoming@patchwork.ozlabs.org>; Thu,  2 Apr 2026 09:19:46 +0000 (UTC)","from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id C42F7382F20;\n\tThu,  2 Apr 2026 09:19:44 +0000 (UTC)","from zg8tmtyylji0my4xnjeumjiw.icoremail.net\n (zg8tmtyylji0my4xnjeumjiw.icoremail.net [162.243.161.220])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id B57F1382F02;\n\tThu,  2 Apr 2026 09:19:39 +0000 (UTC)","from E0005152DT.eswin.cn (unknown [10.12.96.41])\n\tby app2 (Coremail) with SMTP id TQJkCgA3TaCZNM5p1AwOAA--.63708S2;\n\tThu, 02 Apr 2026 17:19:23 +0800 (CST)"],"ARC-Seal":"i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1775121584; cv=none;\n b=i+C0u442JkPMz5iLMOO0DUqakI9Ggi8Rf7EQtnyYzitpFAtYuBe0ioSq33LoD+mBY2odI/D/bKqvTcKgQIlBr/t8G/nN08lTYxD3x1YOU5RJcgU3wrVRbUTuBtTwNvpYK828TLD93zFjHIcKoitOIPfbRLke+s8h9SHMz9XZQOM=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1775121584; c=relaxed/simple;\n\tbh=oBwodQZnYkVNUpmAIYafFb7DwOPd2cZ+nfGOi5zocvU=;\n\th=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References:\n\t MIME-Version;\n b=b2sJtCJ2HoJCL2S+24BDhWCDxrXCg6ER2gn2xDCqfvtu+iQtitcyzrUIAM1rDePTn30M0ui6AG94RN1+PvOkIhD2haFbQacL0xMPs1J2h3DApECK/kiQAZGpF46Fygs0FavUQaJvU+5MYcTZJbsDBv6d8qA920PWrRZoGR4G9Q4=","ARC-Authentication-Results":"i=1; smtp.subspace.kernel.org;\n dmarc=none (p=none dis=none) header.from=eswincomputing.com;\n spf=pass smtp.mailfrom=eswincomputing.com;\n arc=none smtp.client-ip=162.243.161.220","From":"dongxuyang@eswincomputing.com","To":"ukleinek@kernel.org,\n\trobh@kernel.org,\n\tkrzk+dt@kernel.org,\n\tconor+dt@kernel.org,\n\tben-linux@fluff.org,\n\tben.dooks@codethink.co.uk,\n\tp.zabel@pengutronix.de,\n\tlinux-pwm@vger.kernel.org,\n\tdevicetree@vger.kernel.org,\n\tlinux-kernel@vger.kernel.org","Cc":"ningyu@eswincomputing.com,\n\tlinmin@eswincomputing.com,\n\txuxiang@eswincomputing.com,\n\twangguosheng@eswincomputing.com,\n\tpinkesh.vaghela@einfochips.com,\n\tXuyang Dong <dongxuyang@eswincomputing.com>","Subject":"[PATCH v3 2/2] pwm: dwc: add of/platform support","Date":"Thu,  2 Apr 2026 17:19:20 +0800","Message-Id":"<20260402091920.1721-1-dongxuyang@eswincomputing.com>","X-Mailer":"git-send-email 2.31.1.windows.1","In-Reply-To":"<20260402091718.1608-1-dongxuyang@eswincomputing.com>","References":"<20260402091718.1608-1-dongxuyang@eswincomputing.com>","Precedence":"bulk","X-Mailing-List":"linux-pwm@vger.kernel.org","List-Id":"<linux-pwm.vger.kernel.org>","List-Subscribe":"<mailto:linux-pwm+subscribe@vger.kernel.org>","List-Unsubscribe":"<mailto:linux-pwm+unsubscribe@vger.kernel.org>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","X-CM-TRANSID":"TQJkCgA3TaCZNM5p1AwOAA--.63708S2","X-Coremail-Antispam":"1UD129KBjvAXoW3ur1rZF1Utr48XFW3uryxuFg_yoW8Ar18Ao\n\tWxKr1fXw18K3Z3J392ka42gayjvw4kt34fCr1rWF4DC3Z8Zw15AFWUK34Ygw1Sqw1YyFWx\n\tAr4xXr1fAF4fJw18n29KB7ZKAUJUUUU8529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3\n\tAaLaJ3UjIYCTnIWjp_UUUYK7AC8VAFwI0_Gr0_Xr1l1xkIjI8I6I8E6xAIw20EY4v20xva\n\tj40_Wr0E3s1l1IIY67AEw4v_Jr0_Jr4l8cAvFVAK0II2c7xJM28CjxkF64kEwVA0rcxSw2\n\tx7M28EF7xvwVC0I7IYx2IY67AKxVW7JVWDJwA2z4x0Y4vE2Ix0cI8IcVCY1x0267AKxVW8\n\tJr0_Cr1UM28EF7xvwVC2z280aVAFwI0_GcCE3s1l84ACjcxK6I8E87Iv6xkF7I0E14v26r\n\txl6s0DM2AIxVAIcxkEcVAq07x20xvEncxIr21l5I8CrVACY4xI64kE6c02F40Ex7xfMcIj\n\t6xIIjxv20xvE14v26r1Y6r17McIj6I8E87Iv67AKxVWUJVW8JwAm72CE4IkC6x0Yz7v_Jr\n\t0_Gr1lF7xvr2IYc2Ij64vIr41lF7I21c0EjII2zVCS5cI20VAGYxC7M4IIrI8v6xkF7I0E\n\t8cxan2IY04v7M4kE6xkIj40Ew7xC0wCY1x0262kKe7AKxVWUtVW8ZwCY02Avz4vE-syl42\n\txK82IYc2Ij64vIr41l4I8I3I0E4IkC6x0Yz7v_Jr0_Gr1lx2IqxVAqx4xG67AKxVWUJVWU\n\tGwC20s026x8GjcxK67AKxVWUGVWUWwC2zVAF1VAY17CE14v26r4a6rW5MIIYrxkI7VAKI4\n\t8JMIIF0xvE2Ix0cI8IcVAFwI0_Jr0_JF4lIxAIcVC0I7IYx2IY6xkF7I0E14v26r4j6F4U\n\tMIIF0xvE42xK8VAvwI8IcIk0rVWUJVWUCwCI42IY6I8E87Iv67AKxVWUJVW8JwCI42IY6I\n\t8E87Iv6xkF7I0E14v26r4j6r4UJbIYCTnIWIevJa73UjIFyTuYvjfUOEfODUUUU","X-CM-SenderInfo":"pgrqw5xx1d0w46hv4xpqfrz1xxwl0woofrz/"},"content":"From: Xuyang Dong <dongxuyang@eswincomputing.com>\n\nThe dwc pwm controller can be used in non-PCI systems, so allow\neither platform or OF based probing.\n\nThe controller is reset only when no PWM channel is enabled.\nOtherwise, clocks are enabled and the runtime PM state is updated\nto reflect the active hardware configuration.\n\nCo-developed-by: Ben Dooks <ben.dooks@codethink.co.uk>\nSigned-off-by: Ben Dooks <ben.dooks@codethink.co.uk>\nSigned-off-by: Xiang Xu <xuxiang@eswincomputing.com>\nSigned-off-by: Guosheng Wang <wangguosheng@eswincomputing.com>\nSigned-off-by: Xuyang Dong <dongxuyang@eswincomputing.com>\n---\n drivers/pwm/Kconfig        |  10 ++\n drivers/pwm/Makefile       |   1 +\n drivers/pwm/pwm-dwc-core.c | 101 ++++++++---\n drivers/pwm/pwm-dwc-of.c   | 331 +++++++++++++++++++++++++++++++++++++\n drivers/pwm/pwm-dwc.h      |  25 ++-\n 5 files changed, 439 insertions(+), 29 deletions(-)\n create mode 100644 drivers/pwm/pwm-dwc-of.c\n\n--\n2.34.1","diff":"diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig\nindex 6f3147518376..50aea24b6168 100644\n--- a/drivers/pwm/Kconfig\n+++ b/drivers/pwm/Kconfig\n@@ -249,6 +249,16 @@ config PWM_DWC\n \t  To compile this driver as a module, choose M here: the module\n \t  will be called pwm-dwc.\n\n+config PWM_DWC_OF\n+\ttristate \"DesignWare PWM Controller (OF bus)\"\n+\tdepends on HAS_IOMEM && (OF || COMPILE_TEST)\n+\tselect PWM_DWC_CORE\n+\thelp\n+\t  PWM driver for Synopsys DWC PWM Controller on an OF bus or\n+\t  a platform bus.\n+\t  To compile this driver as a module, choose M here: the module\n+\t  will be called pwm-dwc-of.\n+\n config PWM_EP93XX\n \ttristate \"Cirrus Logic EP93xx PWM support\"\n \tdepends on ARCH_EP93XX || COMPILE_TEST\ndiff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile\nindex 0dc0d2b69025..470411a7e5ea 100644\n--- a/drivers/pwm/Makefile\n+++ b/drivers/pwm/Makefile\n@@ -20,6 +20,7 @@ obj-$(CONFIG_PWM_CRC)\t\t+= pwm-crc.o\n obj-$(CONFIG_PWM_CROS_EC)\t+= pwm-cros-ec.o\n obj-$(CONFIG_PWM_DWC_CORE)\t+= pwm-dwc-core.o\n obj-$(CONFIG_PWM_DWC)\t\t+= pwm-dwc.o\n+obj-$(CONFIG_PWM_DWC_OF)\t+= pwm-dwc-of.o\n obj-$(CONFIG_PWM_EP93XX)\t+= pwm-ep93xx.o\n obj-$(CONFIG_PWM_FSL_FTM)\t+= pwm-fsl-ftm.o\n obj-$(CONFIG_PWM_GPIO)\t\t+= pwm-gpio.o\ndiff --git a/drivers/pwm/pwm-dwc-core.c b/drivers/pwm/pwm-dwc-core.c\nindex 6dabec93a3c6..55dd503842c3 100644\n--- a/drivers/pwm/pwm-dwc-core.c\n+++ b/drivers/pwm/pwm-dwc-core.c\n@@ -12,6 +12,7 @@\n #define DEFAULT_SYMBOL_NAMESPACE \"dwc_pwm\"\n\n #include <linux/bitops.h>\n+#include <linux/clk.h>\n #include <linux/export.h>\n #include <linux/kernel.h>\n #include <linux/module.h>\n@@ -44,21 +45,52 @@ static int __dwc_pwm_configure_timer(struct dwc_pwm *dwc,\n \tu32 high;\n \tu32 low;\n\n-\t/*\n-\t * Calculate width of low and high period in terms of input clock\n-\t * periods and check are the result within HW limits between 1 and\n-\t * 2^32 periods.\n-\t */\n-\ttmp = DIV_ROUND_CLOSEST_ULL(state->duty_cycle, dwc->clk_ns);\n-\tif (tmp < 1 || tmp > (1ULL << 32))\n-\t\treturn -ERANGE;\n-\tlow = tmp - 1;\n-\n-\ttmp = DIV_ROUND_CLOSEST_ULL(state->period - state->duty_cycle,\n-\t\t\t\t    dwc->clk_ns);\n-\tif (tmp < 1 || tmp > (1ULL << 32))\n-\t\treturn -ERANGE;\n-\thigh = tmp - 1;\n+\tif (dwc->clk)\n+\t\tdwc->clk_rate = clk_get_rate(dwc->clk);\n+\n+\tif (dwc->features & DWC_TIM_CTRL_0N100PWM_EN) {\n+\t\t/*\n+\t\t * Calculate width of low and high period in terms of input\n+\t\t * clock periods and check are the result within HW limits\n+\t\t * between 0 and 2^32 periods.\n+\t\t */\n+\t\ttmp = state->duty_cycle * dwc->clk_rate;\n+\t\ttmp = DIV_ROUND_UP_ULL(tmp, NSEC_PER_SEC);\n+\t\tif (tmp >= (1ULL << 32))\n+\t\t\treturn -ERANGE;\n+\n+\t\tif (pwm->args.polarity == PWM_POLARITY_INVERSED)\n+\t\t\thigh = tmp;\n+\t\telse\n+\t\t\tlow = tmp;\n+\n+\t\ttmp = (state->period - state->duty_cycle) * dwc->clk_rate;\n+\t\ttmp = DIV_ROUND_UP_ULL(tmp, NSEC_PER_SEC);\n+\t\tif (tmp >= (1ULL << 32))\n+\t\t\treturn -ERANGE;\n+\n+\t\tif (pwm->args.polarity == PWM_POLARITY_INVERSED)\n+\t\t\tlow = tmp;\n+\t\telse\n+\t\t\thigh = tmp;\n+\t} else {\n+\t\t/*\n+\t\t * Calculate width of low and high period in terms of input\n+\t\t * clock periods and check are the result within HW limits\n+\t\t * between 1 and 2^32 periods.\n+\t\t */\n+\t\ttmp = state->duty_cycle * dwc->clk_rate;\n+\t\ttmp = DIV_ROUND_UP_ULL(tmp, NSEC_PER_SEC);\n+\t\tif (tmp < 1 || tmp > (1ULL << 32))\n+\t\t\treturn -ERANGE;\n+\t\tlow = tmp - 1;\n+\n+\t\ttmp = (state->period - state->duty_cycle) * dwc->clk_rate;\n+\t\ttmp = DIV_ROUND_UP_ULL(tmp, NSEC_PER_SEC);\n+\t\tif (tmp < 1 || tmp > (1ULL << 32))\n+\t\t\treturn -ERANGE;\n+\t\thigh = tmp - 1;\n+\t}\n\n \t/*\n \t * Specification says timer usage flow is to disable timer, then\n@@ -74,6 +106,7 @@ static int __dwc_pwm_configure_timer(struct dwc_pwm *dwc,\n \t * width of low period and latter the width of high period in terms\n \t * multiple of input clock periods:\n \t * Width = ((Count + 1) * input clock period).\n+\t * Width = (Count * input clock period) : supported 0% and 100%).\n \t */\n \tdwc_pwm_writel(dwc, low, DWC_TIM_LD_CNT(pwm->hwpwm));\n \tdwc_pwm_writel(dwc, high, DWC_TIM_LD_CNT2(pwm->hwpwm));\n@@ -85,6 +118,9 @@ static int __dwc_pwm_configure_timer(struct dwc_pwm *dwc,\n \t * periods are set by Load Count registers.\n \t */\n \tctrl = DWC_TIM_CTRL_MODE_USER | DWC_TIM_CTRL_PWM;\n+\tif (dwc->features & DWC_TIM_CTRL_0N100PWM_EN)\n+\t\tctrl |= DWC_TIM_CTRL_0N100PWM_EN;\n+\n \tdwc_pwm_writel(dwc, ctrl, DWC_TIM_CTRL(pwm->hwpwm));\n\n \t/*\n@@ -121,11 +157,17 @@ static int dwc_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,\n \t\t\t     struct pwm_state *state)\n {\n \tstruct dwc_pwm *dwc = to_dwc_pwm(chip);\n+\tunsigned long clk_rate;\n \tu64 duty, period;\n \tu32 ctrl, ld, ld2;\n\n \tpm_runtime_get_sync(pwmchip_parent(chip));\n\n+\tif (dwc->clk)\n+\t\tdwc->clk_rate = clk_get_rate(dwc->clk);\n+\n+\tclk_rate = dwc->clk_rate;\n+\n \tctrl = dwc_pwm_readl(dwc, DWC_TIM_CTRL(pwm->hwpwm));\n \tld = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT(pwm->hwpwm));\n \tld2 = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT2(pwm->hwpwm));\n@@ -137,17 +179,32 @@ static int dwc_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,\n \t * based on the timer load-count only.\n \t */\n \tif (ctrl & DWC_TIM_CTRL_PWM) {\n-\t\tduty = (ld + 1) * dwc->clk_ns;\n-\t\tperiod = (ld2 + 1)  * dwc->clk_ns;\n-\t\tperiod += duty;\n+\t\tif (dwc->features & DWC_TIM_CTRL_0N100PWM_EN) {\n+\t\t\tif (pwm->args.polarity == PWM_POLARITY_INVERSED)\n+\t\t\t\tduty = ld2;\n+\t\t\telse\n+\t\t\t\tduty = ld;\n+\t\t\tperiod = (u64)ld + ld2;\n+\t\t} else {\n+\t\t\tduty = ld + 1;\n+\t\t\tperiod = ld2 + 1;\n+\t\t\tperiod += duty;\n+\t\t}\n \t} else {\n-\t\tduty = (ld + 1) * dwc->clk_ns;\n+\t\tduty = ld + 1;\n \t\tperiod = duty * 2;\n \t}\n\n \tstate->polarity = PWM_POLARITY_INVERSED;\n-\tstate->period = period;\n-\tstate->duty_cycle = duty;\n+\t/*\n+\t * If the ld register is at its maximum value. The duty value is\n+\t * 4,294,967,295 (0xFFFF FFFF). The product (duty * NSEC_PER_SEC)\n+\t * is guaranteed to be less than 2^64.\n+\t */\n+\tduty *= NSEC_PER_SEC;\n+\tperiod *= NSEC_PER_SEC;\n+\tstate->period = DIV_ROUND_UP_ULL(period, clk_rate);\n+\tstate->duty_cycle = DIV_ROUND_UP_ULL(duty, clk_rate);\n\n \tpm_runtime_put_sync(pwmchip_parent(chip));\n\n@@ -169,7 +226,7 @@ struct pwm_chip *dwc_pwm_alloc(struct device *dev)\n \t\treturn chip;\n \tdwc = to_dwc_pwm(chip);\n\n-\tdwc->clk_ns = 10;\n+\tdwc->clk_rate = NSEC_PER_SEC / 10;\n \tchip->ops = &dwc_pwm_ops;\n\n \treturn chip;\ndiff --git a/drivers/pwm/pwm-dwc-of.c b/drivers/pwm/pwm-dwc-of.c\nnew file mode 100644\nindex 000000000000..dc3361f44121\n--- /dev/null\n+++ b/drivers/pwm/pwm-dwc-of.c\n@@ -0,0 +1,331 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/*\n+ * DesignWare PWM Controller driver OF\n+ *\n+ * Copyright (C) 2026 SiFive, Inc.\n+ */\n+\n+#define DEFAULT_SYMBOL_NAMESPACE \"dwc_pwm_of\"\n+\n+#include <linux/clk.h>\n+#include <linux/platform_device.h>\n+#include <linux/pm_runtime.h>\n+#include <linux/pwm.h>\n+#include <linux/reset.h>\n+\n+#include \"pwm-dwc.h\"\n+\n+static int dwc_pwm_plat_probe(struct platform_device *pdev)\n+{\n+\tstruct device *dev = &pdev->dev;\n+\tstruct dwc_pwm_drvdata *data;\n+\tu32 ctrl[DWC_TIMERS_TOTAL];\n+\tstruct pwm_chip *chip;\n+\tstruct dwc_pwm *dwc;\n+\tbool pwm_en = false;\n+\tu32 nr_pwm, tim_id;\n+\tunsigned int i;\n+\tint ret;\n+\n+\tdata = devm_kzalloc(dev, struct_size(data, chips, 1), GFP_KERNEL);\n+\tif (!data)\n+\t\treturn -ENOMEM;\n+\n+\tchip = dwc_pwm_alloc(dev);\n+\tif (IS_ERR(chip))\n+\t\treturn dev_err_probe(dev, -ENOMEM, \"failed to alloc pwm\\n\");\n+\n+\tdwc = to_dwc_pwm(chip);\n+\n+\tdwc->base = devm_platform_ioremap_resource(pdev, 0);\n+\tif (IS_ERR(dwc->base))\n+\t\treturn PTR_ERR(dwc->base);\n+\n+\tif (!device_property_read_u32(dev, \"snps,pwm-number\", &nr_pwm)) {\n+\t\tif (nr_pwm > DWC_TIMERS_TOTAL)\n+\t\t\tdev_warn\n+\t\t\t(dev, \"too many PWMs (%d) specified, capping at %d\\n\",\n+\t\t\tnr_pwm, chip->npwm);\n+\t\telse\n+\t\t\tchip->npwm = nr_pwm;\n+\t}\n+\n+\tdwc->bus_clk = devm_clk_get(dev, \"bus\");\n+\tif (IS_ERR(dwc->bus_clk))\n+\t\treturn dev_err_probe(dev, PTR_ERR(dwc->bus_clk),\n+\t\t\t\t     \"failed to get bus clock\\n\");\n+\n+\tdwc->clk = devm_clk_get(dev, \"timer\");\n+\tif (IS_ERR(dwc->clk))\n+\t\treturn dev_err_probe(dev, PTR_ERR(dwc->clk),\n+\t\t\t\t     \"failed to get timer clock\\n\");\n+\n+\tret = devm_clk_rate_exclusive_get(dev, dwc->clk);\n+\tif (ret)\n+\t\treturn dev_err_probe(dev, ret,\n+\t\t\t\t     \"failed to get exclusive rate\\n\");\n+\n+\tdwc->clk_rate = clk_get_rate(dwc->clk);\n+\n+\tdwc->rst = devm_reset_control_get_optional_exclusive(dev, NULL);\n+\tif (IS_ERR(dwc->rst))\n+\t\treturn dev_err_probe(dev, PTR_ERR(dwc->rst),\n+\t\t\t\t     \"failed to get reset control\\n\");\n+\n+\tret = clk_prepare_enable(dwc->bus_clk);\n+\tif (ret)\n+\t\treturn dev_err_probe(dev, ret,\n+\t\t\t\t     \"failed to enable bus clock\\n\");\n+\n+\tret = clk_prepare_enable(dwc->clk);\n+\tif (ret) {\n+\t\tdev_err(dev, \"failed to enable timer clock\\n\");\n+\t\tgoto disable_busclk;\n+\t}\n+\n+\t/*\n+\t * Check all channels to see if any channel is enabled.\n+\t * Read the control register of each channel and extract the enable bit\n+\t */\n+\tfor (i = 0; i < chip->npwm; i++) {\n+\t\tctrl[i] = dwc_pwm_readl(dwc, DWC_TIM_CTRL(i)) & DWC_TIM_CTRL_EN;\n+\t\tif (ctrl[i])\n+\t\t\tpwm_en = true;\n+\t}\n+\n+\t/* Only issue reset when all channels are disabled */\n+\tif (!pwm_en) {\n+\t\tret = reset_control_reset(dwc->rst);\n+\t\tif (ret) {\n+\t\t\tdev_err(dev, \"failed to reset\\n\");\n+\t\t\tgoto disable_clk;\n+\t\t}\n+\t}\n+\n+\t/* init PWM feature */\n+\tdwc->features = 0;\n+\t/*\n+\t * Support for 0% and 100% duty cycle mode was added in version 2.11a\n+\t * and later.\n+\t */\n+\ttim_id = dwc_pwm_readl(dwc, DWC_TIMERS_COMP_VERSION);\n+\tif (tim_id >= DWC_TIM_VERSION_ID_2_11A)\n+\t\tdwc->features |= DWC_TIM_CTRL_0N100PWM_EN;\n+\n+\tret = devm_pwmchip_add(dev, chip);\n+\tif (ret) {\n+\t\tdev_err(dev, \"failed to add pwm chip\");\n+\t\tgoto reset_assert;\n+\t}\n+\n+\tdata->chips[0] = chip;\n+\tdev_set_drvdata(dev, data);\n+\n+\t/*\n+\t * If any PWM channel is enabled, mark device active and hold runtime PM\n+\t * references for each enabled channel. Otherwise, gate the clocks.\n+\t */\n+\tif (pwm_en) {\n+\t\tpm_runtime_set_active(dev);\n+\t\tfor (i = 0; i < chip->npwm; i++) {\n+\t\t\tif (ctrl[i])\n+\t\t\t\tpm_runtime_get_noresume(dev);\n+\t\t}\n+\t} else {\n+\t\tclk_disable_unprepare(dwc->clk);\n+\t\tclk_disable_unprepare(dwc->bus_clk);\n+\t}\n+\n+\tpm_runtime_enable(dev);\n+\n+\treturn 0;\n+\n+reset_assert:\n+\treset_control_assert(dwc->rst);\n+disable_clk:\n+\tclk_disable_unprepare(dwc->clk);\n+disable_busclk:\n+\tclk_disable_unprepare(dwc->bus_clk);\n+\n+\treturn ret;\n+}\n+\n+static void dwc_pwm_plat_remove(struct platform_device *pdev)\n+{\n+\tstruct dwc_pwm_drvdata *data = platform_get_drvdata(pdev);\n+\tstruct pwm_chip *chip = data->chips[0];\n+\tstruct dwc_pwm *dwc = to_dwc_pwm(chip);\n+\tbool pwm_en = false;\n+\tunsigned int idx;\n+\tbool pm_flags;\n+\n+\t/*\n+\t * Resume the device if it is runtime suspended to allow\n+\t * safe register access.\n+\t */\n+\tpm_flags = pm_runtime_status_suspended(&pdev->dev);\n+\tif (pm_flags)\n+\t\tpm_runtime_get_sync(&pdev->dev);\n+\n+\tfor (idx = 0; idx < chip->npwm; idx++) {\n+\t\tif (dwc_pwm_readl(dwc, DWC_TIM_CTRL(idx)) & DWC_TIM_CTRL_EN) {\n+\t\t\tpwm_en = true;\n+\t\t\tpm_runtime_put_noidle(&pdev->dev);\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * Re-suspend the device if it was runtime suspended prior to\n+\t * the register access.\n+\t */\n+\tif (pm_flags)\n+\t\tpm_runtime_put_sync(&pdev->dev);\n+\n+\tif (pwm_en) {\n+\t\tclk_disable_unprepare(dwc->clk);\n+\t\tclk_disable_unprepare(dwc->bus_clk);\n+\t}\n+\n+\tpm_runtime_disable(&pdev->dev);\n+\treset_control_assert(dwc->rst);\n+}\n+\n+static int dwc_pwm_runtime_suspend(struct device *dev)\n+{\n+\tstruct dwc_pwm_drvdata *data = dev_get_drvdata(dev);\n+\tstruct pwm_chip *chip = data->chips[0];\n+\tstruct dwc_pwm *dwc = to_dwc_pwm(chip);\n+\n+\tclk_disable_unprepare(dwc->clk);\n+\tclk_disable_unprepare(dwc->bus_clk);\n+\n+\treturn 0;\n+}\n+\n+static int dwc_pwm_runtime_resume(struct device *dev)\n+{\n+\tstruct dwc_pwm_drvdata *data = dev_get_drvdata(dev);\n+\tstruct pwm_chip *chip = data->chips[0];\n+\tstruct dwc_pwm *dwc = to_dwc_pwm(chip);\n+\tint ret;\n+\n+\tret = clk_prepare_enable(dwc->bus_clk);\n+\tif (ret) {\n+\t\tdev_err(dev, \"failed to enable bus clock: %d\\n\", ret);\n+\t\treturn ret;\n+\t}\n+\n+\tret = clk_prepare_enable(dwc->clk);\n+\tif (ret) {\n+\t\tdev_err(dev, \"failed to enable timer clock: %d\\n\", ret);\n+\t\tclk_disable_unprepare(dwc->bus_clk);\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int dwc_pwm_suspend(struct device *dev)\n+{\n+\tstruct dwc_pwm_drvdata *data = dev_get_drvdata(dev);\n+\tstruct pwm_chip *chip = data->chips[0];\n+\tstruct dwc_pwm *dwc = to_dwc_pwm(chip);\n+\tunsigned int idx;\n+\tint ret;\n+\n+\tif (pm_runtime_status_suspended(dev)) {\n+\t\tret = dwc_pwm_runtime_resume(dev);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tfor (idx = 0; idx < chip->npwm; idx++) {\n+\t\tif (chip->pwms[idx].state.enabled)\n+\t\t\treturn -EBUSY;\n+\n+\t\tdwc->ctx[idx].cnt = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT(idx));\n+\t\tdwc->ctx[idx].cnt2 = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT2(idx));\n+\t\tdwc->ctx[idx].ctrl = dwc_pwm_readl(dwc, DWC_TIM_CTRL(idx));\n+\t}\n+\n+\tret = dwc_pwm_runtime_suspend(dev);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn 0;\n+}\n+\n+static int dwc_pwm_resume(struct device *dev)\n+{\n+\tstruct dwc_pwm_drvdata *data = dev_get_drvdata(dev);\n+\tstruct pwm_chip *chip = data->chips[0];\n+\tstruct dwc_pwm *dwc = to_dwc_pwm(chip);\n+\tunsigned int idx;\n+\tbool pm_flags;\n+\tint ret;\n+\n+\t/* Check if device was runtime suspended before system resume */\n+\tpm_flags = pm_runtime_status_suspended(dev);\n+\tif (pm_flags) {\n+\t\t/*\n+\t\t * Use PM framework to resume device\n+\t\t * (calls dwc_pwm_runtime_resume)\n+\t\t */\n+\t\tret = pm_runtime_get_sync(dev);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t} else {\n+\t\t/*\n+\t\t * Device was active, but clocks might be off after system sleep\n+\t\t * Call runtime_resume directly to restore hardware state\n+\t\t */\n+\t\tret = dwc_pwm_runtime_resume(dev);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tfor (idx = 0; idx < chip->npwm; idx++) {\n+\t\tdwc_pwm_writel(dwc, dwc->ctx[idx].cnt, DWC_TIM_LD_CNT(idx));\n+\t\tdwc_pwm_writel(dwc, dwc->ctx[idx].cnt2, DWC_TIM_LD_CNT2(idx));\n+\t\tdwc_pwm_writel(dwc, dwc->ctx[idx].ctrl, DWC_TIM_CTRL(idx));\n+\t}\n+\n+\tif (pm_flags) {\n+\t\t/* Balance the refcount taken by pm_runtime_get_sync\n+\t\t * if it was used\n+\t\t */\n+\t\tret = pm_runtime_put_sync(dev);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static const struct dev_pm_ops dwc_pwm_pm_ops = {\n+\tRUNTIME_PM_OPS(dwc_pwm_runtime_suspend, dwc_pwm_runtime_resume, NULL)\n+\tSYSTEM_SLEEP_PM_OPS(dwc_pwm_suspend, dwc_pwm_resume)\n+};\n+\n+static const struct of_device_id dwc_pwm_dt_ids[] = {\n+\t{ .compatible = \"snps,dw-apb-timers-pwm2\" },\n+\t{ }\n+};\n+MODULE_DEVICE_TABLE(of, dwc_pwm_dt_ids);\n+\n+static struct platform_driver dwc_pwm_plat_driver = {\n+\t.driver = {\n+\t\t.name = \"dwc-pwm\",\n+\t\t.pm = pm_ptr(&dwc_pwm_pm_ops),\n+\t\t.of_match_table = dwc_pwm_dt_ids,\n+\t},\n+\t.probe = dwc_pwm_plat_probe,\n+\t.remove = dwc_pwm_plat_remove,\n+};\n+\n+module_platform_driver(dwc_pwm_plat_driver);\n+\n+MODULE_ALIAS(\"platform:dwc-pwm-of\");\n+MODULE_AUTHOR(\"Ben Dooks <ben.dooks@codethink.co.uk>\");\n+MODULE_DESCRIPTION(\"DesignWare PWM Controller\");\n+MODULE_LICENSE(\"GPL\");\ndiff --git a/drivers/pwm/pwm-dwc.h b/drivers/pwm/pwm-dwc.h\nindex 1562594e7f85..75f7c2d031c4 100644\n--- a/drivers/pwm/pwm-dwc.h\n+++ b/drivers/pwm/pwm-dwc.h\n@@ -26,12 +26,19 @@ MODULE_IMPORT_NS(\"dwc_pwm\");\n #define DWC_TIMERS_TOTAL\t8\n\n /* Timer Control Register */\n-#define DWC_TIM_CTRL_EN\t\tBIT(0)\n-#define DWC_TIM_CTRL_MODE\tBIT(1)\n-#define DWC_TIM_CTRL_MODE_FREE\t(0 << 1)\n-#define DWC_TIM_CTRL_MODE_USER\t(1 << 1)\n-#define DWC_TIM_CTRL_INT_MASK\tBIT(2)\n-#define DWC_TIM_CTRL_PWM\tBIT(3)\n+#define DWC_TIM_CTRL_EN\t\t\tBIT(0)\n+#define DWC_TIM_CTRL_MODE\t\tBIT(1)\n+#define DWC_TIM_CTRL_MODE_FREE\t\t(0 << 1)\n+#define DWC_TIM_CTRL_MODE_USER\t\tBIT(1)\n+#define DWC_TIM_CTRL_INT_MASK\t\tBIT(2)\n+#define DWC_TIM_CTRL_PWM\t\tBIT(3)\n+#define DWC_TIM_CTRL_0N100PWM_EN\tBIT(4)\n+\n+/*\n+ * The version 2.11a and later add \"Pulse Width Modulation with\n+ * 0% and 100% Duty Cycle\".\n+ */\n+#define DWC_TIM_VERSION_ID_2_11A\t0x3231312a\n\n struct dwc_pwm_info {\n \tunsigned int nr;\n@@ -52,8 +59,12 @@ struct dwc_pwm_ctx {\n\n struct dwc_pwm {\n \tvoid __iomem *base;\n-\tunsigned int clk_ns;\n+\tstruct clk *bus_clk;\n+\tstruct clk *clk;\n+\tunsigned long clk_rate;\n+\tstruct reset_control *rst;\n \tstruct dwc_pwm_ctx ctx[DWC_TIMERS_TOTAL];\n+\tu32 features;\n };\n\n static inline struct dwc_pwm *to_dwc_pwm(struct pwm_chip *chip)\n","prefixes":["v3","2/2"]}