* [PATCH 0/2] Add driver support for ESWIN EIC7700 PWM controller @ 2025-12-05 9:04 dongxuyang 2025-12-05 9:04 ` [PATCH 1/2] dt-bindings: pwm: eswin: Add EIC7700 pwm controller dongxuyang 2025-12-05 9:05 ` [PATCH 2/2] pwm: eswin: Add EIC7700 pwm driver dongxuyang 0 siblings, 2 replies; 8+ messages in thread From: dongxuyang @ 2025-12-05 9:04 UTC (permalink / raw) To: ukleinek, robh, krzk+dt, conor+dt, p.zabel, linux-pwm, devicetree, linux-kernel Cc: ningyu, linmin, xuxiang, wangguosheng, pinkesh.vaghela, Xuyang Dong From: Xuyang Dong <dongxuyang@eswincomputing.com> Add support for the ESWIN EIC7700 PWM (Pulse Width Modulation) based on Synopsys DWC PWM. Features: The EIC7700 PWM driver supports a duty cycle range from 0% to 100%, with explicit support added for both 0% and 100% duty cycles. Supported chips: ESWIN EIC7700 series SoC. Test: Tested this patch on the Sifive HiFive Premier P550 (which used the EIC7700 SoC). Xuyang Dong (2): dt-bindings: pwm: eswin: Add EIC7700 pwm controller pwm: eswin: Add EIC7700 pwm driver .../bindings/pwm/eswin,eic7700-pwm.yaml | 73 ++++++ drivers/pwm/Kconfig | 12 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-dwc-core.c | 76 ++++-- drivers/pwm/pwm-dwc-eic7700.c | 231 ++++++++++++++++++ drivers/pwm/pwm-dwc.h | 16 +- 6 files changed, 384 insertions(+), 25 deletions(-) create mode 100644 Documentation/devicetree/bindings/pwm/eswin,eic7700-pwm.yaml create mode 100644 drivers/pwm/pwm-dwc-eic7700.c -- 2.34.1 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/2] dt-bindings: pwm: eswin: Add EIC7700 pwm controller 2025-12-05 9:04 [PATCH 0/2] Add driver support for ESWIN EIC7700 PWM controller dongxuyang @ 2025-12-05 9:04 ` dongxuyang 2025-12-08 6:45 ` Krzysztof Kozlowski 2025-12-05 9:05 ` [PATCH 2/2] pwm: eswin: Add EIC7700 pwm driver dongxuyang 1 sibling, 1 reply; 8+ messages in thread From: dongxuyang @ 2025-12-05 9:04 UTC (permalink / raw) To: ukleinek, robh, krzk+dt, conor+dt, p.zabel, linux-pwm, devicetree, linux-kernel Cc: ningyu, linmin, xuxiang, wangguosheng, pinkesh.vaghela, Xuyang Dong From: Xuyang Dong <dongxuyang@eswincomputing.com> Introduce device-tree binding documentation for ESWIN EIC7700 pwm controller. Signed-off-by: Xiang Xu <xuxiang@eswincomputing.com> Signed-off-by: Guosheng Wang <wangguosheng@eswincomputing.com> Signed-off-by: Xuyang Dong <dongxuyang@eswincomputing.com> --- .../bindings/pwm/eswin,eic7700-pwm.yaml | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/eswin,eic7700-pwm.yaml diff --git a/Documentation/devicetree/bindings/pwm/eswin,eic7700-pwm.yaml b/Documentation/devicetree/bindings/pwm/eswin,eic7700-pwm.yaml new file mode 100644 index 000000000000..8b7dc7d4dffe --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/eswin,eic7700-pwm.yaml @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/eswin,eic7700-pwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ESWIN EIC7700 PWM controller + +maintainers: + - Xiang Xu <xuxiang@eswincomputing.com> + - Guosheng Wang <wangguosheng@eswincomputing.com> + - Xuyang Dong <dongxuyang@eswincomputing.com> + +description: | + The EIC7700 PWM used the DesignWare APB timers module. The PWM driver + supports a duty cycle range from 0% to 100%, with explicit support for + both 0% and 100% duty cycles. + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + const: eswin,eic7700-pwm + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + + "#pwm-cells": + const: 3 + + pinctrl-0: true + pinctrl-1: true + + pinctrl-names: + minItems: 1 + items: + - const: default + - const: sleep + + snps,pwm-full-range-enable: + type: boolean + description: | + Enable support for 0% and 100% duty cycle. + When present, the PWM controller can output 0% and 100% duty cycles. + +required: + - compatible + - reg + - clocks + - resets + - snps,pwm-full-range-enable + +additionalProperties: false + +examples: + - | + pwm@50818000 { + compatible = "eswin,eic7700-pwm"; + reg = <0x50818000 0x4000>; + clocks = <&clock 242>; + resets = <&reset 136>; + #pwm-cells = <3>; + pinctrl-names = "default"; + pinctrl-0 = <&fan_pwm>; + snps,pwm-full-range-enable; + }; -- 2.34.1 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] dt-bindings: pwm: eswin: Add EIC7700 pwm controller 2025-12-05 9:04 ` [PATCH 1/2] dt-bindings: pwm: eswin: Add EIC7700 pwm controller dongxuyang @ 2025-12-08 6:45 ` Krzysztof Kozlowski 2025-12-09 10:20 ` Xuyang Dong 0 siblings, 1 reply; 8+ messages in thread From: Krzysztof Kozlowski @ 2025-12-08 6:45 UTC (permalink / raw) To: dongxuyang Cc: ukleinek, robh, krzk+dt, conor+dt, p.zabel, linux-pwm, devicetree, linux-kernel, ningyu, linmin, xuxiang, wangguosheng, pinkesh.vaghela On Fri, Dec 05, 2025 at 05:04:50PM +0800, dongxuyang@eswincomputing.com wrote: > diff --git a/Documentation/devicetree/bindings/pwm/eswin,eic7700-pwm.yaml b/Documentation/devicetree/bindings/pwm/eswin,eic7700-pwm.yaml > new file mode 100644 > index 000000000000..8b7dc7d4dffe > --- /dev/null > +++ b/Documentation/devicetree/bindings/pwm/eswin,eic7700-pwm.yaml > @@ -0,0 +1,73 @@ > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) > +%YAML 1.2 > +--- > +$id: http://devicetree.org/schemas/pwm/eswin,eic7700-pwm.yaml# > +$schema: http://devicetree.org/meta-schemas/core.yaml# > + > +title: ESWIN EIC7700 PWM controller > + > +maintainers: > + - Xiang Xu <xuxiang@eswincomputing.com> > + - Guosheng Wang <wangguosheng@eswincomputing.com> > + - Xuyang Dong <dongxuyang@eswincomputing.com> > + > +description: | Do not need '|' unless you need to preserve formatting. > + The EIC7700 PWM used the DesignWare APB timers module. The PWM driver > + supports a duty cycle range from 0% to 100%, with explicit support for Driver is irrelevant here. Describe hardware. > + both 0% and 100% duty cycles. > + > +allOf: > + - $ref: pwm.yaml# > + > +properties: > + compatible: > + const: eswin,eic7700-pwm > + > + reg: > + maxItems: 1 > + > + clocks: > + maxItems: 1 > + > + resets: > + maxItems: 1 > + > + "#pwm-cells": > + const: 3 > + > + pinctrl-0: true > + pinctrl-1: true > + > + pinctrl-names: > + minItems: 1 > + items: > + - const: default > + - const: sleep > + > + snps,pwm-full-range-enable: 1. Wrong vendor prefix, thats eswin, not snps. 2. Why is this a hardware property? I really do not see that. You described the desired Linux feature or behavior, not the actual hardware. The bindings are about the latter, so instead you need to rephrase the property and its description to match actual hardware capabilities/features/configuration etc. Best regards, Krzysztof ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Re: [PATCH 1/2] dt-bindings: pwm: eswin: Add EIC7700 pwm controller 2025-12-08 6:45 ` Krzysztof Kozlowski @ 2025-12-09 10:20 ` Xuyang Dong 2025-12-09 21:40 ` Krzysztof Kozlowski 0 siblings, 1 reply; 8+ messages in thread From: Xuyang Dong @ 2025-12-09 10:20 UTC (permalink / raw) To: Krzysztof Kozlowski, ben.dooks Cc: ukleinek, robh, krzk+dt, conor+dt, p.zabel, linux-pwm, devicetree, linux-kernel, ningyu, linmin, xuxiang, wangguosheng, pinkesh.vaghela > > diff --git a/Documentation/devicetree/bindings/pwm/eswin,eic7700-pwm.yaml b/Documentation/devicetree/bindings/pwm/eswin,eic7700-pwm.yaml > > new file mode 100644 > > index 000000000000..8b7dc7d4dffe > > --- /dev/null > > +++ b/Documentation/devicetree/bindings/pwm/eswin,eic7700-pwm.yaml > > @@ -0,0 +1,73 @@ > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) > > +%YAML 1.2 > > +--- > > +$id: http://devicetree.org/schemas/pwm/eswin,eic7700-pwm.yaml# > > +$schema: http://devicetree.org/meta-schemas/core.yaml# > > + > > +title: ESWIN EIC7700 PWM controller > > + > > +maintainers: > > + - Xiang Xu <xuxiang@eswincomputing.com> > > + - Guosheng Wang <wangguosheng@eswincomputing.com> > > + - Xuyang Dong <dongxuyang@eswincomputing.com> > > + > > +description: | > > Do not need '|' unless you need to preserve formatting. > > > + The EIC7700 PWM used the DesignWare APB timers module. The PWM driver > > + supports a duty cycle range from 0% to 100%, with explicit support for > > Driver is irrelevant here. Describe hardware. > > > + both 0% and 100% duty cycles. > > + > > +allOf: > > + - $ref: pwm.yaml# > > + > > +properties: > > + compatible: > > + const: eswin,eic7700-pwm > > + > > + reg: > > + maxItems: 1 > > + > > + clocks: > > + maxItems: 1 > > + > > + resets: > > + maxItems: 1 > > + > > + "#pwm-cells": > > + const: 3 > > + > > + pinctrl-0: true > > + pinctrl-1: true > > + > > + pinctrl-names: > > + minItems: 1 > > + items: > > + - const: default > > + - const: sleep > > + > > + snps,pwm-full-range-enable: > > 1. Wrong vendor prefix, thats eswin, not snps. > 2. Why is this a hardware property? I really do not see that. You > described the desired Linux feature or behavior, not the actual > hardware. The bindings are about the latter, so instead you need to > rephrase the property and its description to match actual hardware > capabilities/features/configuration etc. > Hi Krzysztof and Ben, There is a patch [1] submmitted by Ben Dook a few years ago. The intention of this patch is to make DWC pwm controller can be used as platform driver. This is what we are currently working on. It seems that it would be more reasonable to continue improving this patch and make the DWC PWM driver universal, rather than creating a separate ESWIN PWM driver. The DWC pwm 2.13a version supports "Pulse Width Modulation with 0% and 100% Duty Cycle" by programming TIMER_0N100PWM_EN bit field. We also want to support this new hardware feature. So, adding a new property, like snps,timer-0n100pwm-en, in pwm/snps,dw-apb-timers-pwm2.yaml would be better? Krzysztof and Ben, do you think the above approach is reasonable and acceptable? [1] https://lore.kernel.org/lkml/20230907161242.67190-7-ben.dooks@codethink.co.uk/ Regards, Xuyang Dong ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] dt-bindings: pwm: eswin: Add EIC7700 pwm controller 2025-12-09 10:20 ` Xuyang Dong @ 2025-12-09 21:40 ` Krzysztof Kozlowski 0 siblings, 0 replies; 8+ messages in thread From: Krzysztof Kozlowski @ 2025-12-09 21:40 UTC (permalink / raw) To: Xuyang Dong, ben.dooks Cc: ukleinek, robh, krzk+dt, conor+dt, p.zabel, linux-pwm, devicetree, linux-kernel, ningyu, linmin, xuxiang, wangguosheng, pinkesh.vaghela On 09/12/2025 11:20, Xuyang Dong wrote: >>> + >>> + pinctrl-0: true >>> + pinctrl-1: true >>> + >>> + pinctrl-names: >>> + minItems: 1 >>> + items: >>> + - const: default >>> + - const: sleep >>> + >>> + snps,pwm-full-range-enable: >> >> 1. Wrong vendor prefix, thats eswin, not snps. >> 2. Why is this a hardware property? I really do not see that. You >> described the desired Linux feature or behavior, not the actual >> hardware. The bindings are about the latter, so instead you need to >> rephrase the property and its description to match actual hardware >> capabilities/features/configuration etc. >> > Hi Krzysztof and Ben, > > There is a patch [1] submmitted by Ben Dook a few years ago. The > intention of this patch is to make DWC pwm controller can be used as > platform driver. This is what we are currently working on. > It seems that it would be more reasonable to continue improving this > patch and make the DWC PWM driver universal, rather than creating a > separate ESWIN PWM driver. > > The DWC pwm 2.13a version supports "Pulse Width Modulation with 0% and > 100% Duty Cycle" by programming TIMER_0N100PWM_EN bit field. We also > want to support this new hardware feature. So, adding a new property, > like snps,timer-0n100pwm-en, in pwm/snps,dw-apb-timers-pwm2.yaml > would be better? > > Krzysztof and Ben, do you think the above approach is reasonable and acceptable? How any of this is related to the binding? We talk here about hardware and binding, but you ask me about driver.... I don't care. Best regards, Krzysztof ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 2/2] pwm: eswin: Add EIC7700 pwm driver 2025-12-05 9:04 [PATCH 0/2] Add driver support for ESWIN EIC7700 PWM controller dongxuyang 2025-12-05 9:04 ` [PATCH 1/2] dt-bindings: pwm: eswin: Add EIC7700 pwm controller dongxuyang @ 2025-12-05 9:05 ` dongxuyang 2025-12-06 10:09 ` kernel test robot 2025-12-08 6:48 ` Krzysztof Kozlowski 1 sibling, 2 replies; 8+ messages in thread From: dongxuyang @ 2025-12-05 9:05 UTC (permalink / raw) To: ukleinek, robh, krzk+dt, conor+dt, p.zabel, linux-pwm, devicetree, linux-kernel Cc: ningyu, linmin, xuxiang, wangguosheng, pinkesh.vaghela, Xuyang Dong From: Xuyang Dong <dongxuyang@eswincomputing.com> Introduce driver for PWM module available on EIC7700 SoC. Add snps,pwm-0-n-100-percent flag to support for both 0% and 100% duty cycles. And remove this flag to support the duty cycle range from 0% to 100%. Signed-off-by: Xiang Xu <xuxiang@eswincomputing.com> Signed-off-by: Guosheng Wang <wangguosheng@eswincomputing.com> Signed-off-by: Xuyang Dong <dongxuyang@eswincomputing.com> --- drivers/pwm/Kconfig | 12 ++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-dwc-core.c | 76 ++++++++--- drivers/pwm/pwm-dwc-eic7700.c | 231 ++++++++++++++++++++++++++++++++++ drivers/pwm/pwm-dwc.h | 16 ++- 5 files changed, 311 insertions(+), 25 deletions(-) create mode 100644 drivers/pwm/pwm-dwc-eic7700.c diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index bf2d101f67a1..c2cf0d9c9854 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -249,6 +249,18 @@ config PWM_DWC To compile this driver as a module, choose M here: the module will be called pwm-dwc. +config PWM_EIC7700 + tristate "EIC7700 PWM Controller" + depends on ARCH_ESWIN || COMPILE_TEST + depends on HAS_IOMEM + select PWM_DWC_CORE + help + PWM driver for Synopsys DWC PWM Controller on a platform for EIC7700 + SoCs. + + To compile this driver as a module, choose M here: the module + will be called pwm-dwc-eic7700. + 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 0dc0d2b69025..2b4b7a5c5b15 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -20,6 +20,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_EIC7700) += pwm-dwc-eic7700.o obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o obj-$(CONFIG_PWM_GPIO) += pwm-gpio.o diff --git a/drivers/pwm/pwm-dwc-core.c b/drivers/pwm/pwm-dwc-core.c index 6dabec93a3c6..ec90f75c840e 100644 --- a/drivers/pwm/pwm-dwc-core.c +++ b/drivers/pwm/pwm-dwc-core.c @@ -44,21 +44,47 @@ static int __dwc_pwm_configure_timer(struct dwc_pwm *dwc, u32 high; u32 low; - /* - * Calculate width of low and high period in terms of input clock - * periods and check are the result within HW limits between 1 and - * 2^32 periods. - */ - tmp = DIV_ROUND_CLOSEST_ULL(state->duty_cycle, dwc->clk_ns); - if (tmp < 1 || tmp > (1ULL << 32)) - return -ERANGE; - low = tmp - 1; - - tmp = DIV_ROUND_CLOSEST_ULL(state->period - state->duty_cycle, - dwc->clk_ns); - if (tmp < 1 || tmp > (1ULL << 32)) - return -ERANGE; - high = tmp - 1; + if (dwc->pwm_0n100_enable) { + /* + * Calculate width of low and high period in terms of input + * clock periods and check are the result within HW limits + * between 0 and 2^32 periods. + */ + tmp = DIV_ROUND_CLOSEST_ULL(state->duty_cycle, dwc->clk_ns); + if (tmp < 0 || tmp > (1UL << 32)) + return -ERANGE; + + if (pwm->args.polarity == PWM_POLARITY_INVERSED) + high = tmp; + else + low = tmp; + + tmp = DIV_ROUND_CLOSEST_ULL(state->period - state->duty_cycle, + dwc->clk_ns); + if (tmp < 0 || tmp > (1UL << 32)) + return -ERANGE; + + if (pwm->args.polarity == PWM_POLARITY_INVERSED) + low = tmp; + else + high = tmp; + } else { + /* + * Calculate width of low and high period in terms of input + * clock periods and check are the result within HW limits + * between 1 and 2^32 periods. + */ + tmp = DIV_ROUND_CLOSEST_ULL(state->duty_cycle, dwc->clk_ns); + if (tmp < 1 || tmp > (1ULL << 32)) + return -ERANGE; + low = tmp - 1; + + tmp = DIV_ROUND_CLOSEST_ULL(state->period - state->duty_cycle, + dwc->clk_ns); + if (tmp < 1 || tmp > (1ULL << 32)) + return -ERANGE; + high = tmp - 1; + } /* * Specification says timer usage flow is to disable timer, then @@ -73,7 +99,8 @@ static int __dwc_pwm_configure_timer(struct dwc_pwm *dwc, * Write Load Count and Load Count 2 registers. Former defines the * width of low period and latter the width of high period in terms * multiple of input clock periods: - * Width = ((Count + 1) * input clock period). + * Width = ((Count + 1) * input clock period) or + * Width = (Count * input clock period) : supported 0% and 100%). */ dwc_pwm_writel(dwc, low, DWC_TIM_LD_CNT(pwm->hwpwm)); dwc_pwm_writel(dwc, high, DWC_TIM_LD_CNT2(pwm->hwpwm)); @@ -85,6 +112,9 @@ static int __dwc_pwm_configure_timer(struct dwc_pwm *dwc, * periods are set by Load Count registers. */ ctrl = DWC_TIM_CTRL_MODE_USER | DWC_TIM_CTRL_PWM; + if (dwc->pwm_0n100_enable) + ctrl |= DWC_TIM_CTRL_0N100PWM_EN; + dwc_pwm_writel(dwc, ctrl, DWC_TIM_CTRL(pwm->hwpwm)); /* @@ -137,9 +167,17 @@ static int dwc_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, * based on the timer load-count only. */ if (ctrl & DWC_TIM_CTRL_PWM) { - duty = (ld + 1) * dwc->clk_ns; - period = (ld2 + 1) * dwc->clk_ns; - period += duty; + if (dwc->pwm_0n100_enable) { + if (pwm->args.polarity == PWM_POLARITY_INVERSED) + duty = ld2 * dwc->clk_ns; + else + duty = ld * dwc->clk_ns; + period = (ld + ld2) * dwc->clk_ns; + } else { + duty = (ld + 1) * dwc->clk_ns; + period = (ld2 + 1) * dwc->clk_ns; + period += duty; + } } else { duty = (ld + 1) * dwc->clk_ns; period = duty * 2; diff --git a/drivers/pwm/pwm-dwc-eic7700.c b/drivers/pwm/pwm-dwc-eic7700.c new file mode 100644 index 000000000000..1bbe5593934b --- /dev/null +++ b/drivers/pwm/pwm-dwc-eic7700.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ESWIN PWM driver (Based on DesignWare PWM Controller driver) + * + * Copyright 2025, Beijing ESWIN Computing Technology Co., Ltd.. + * All rights reserved. + * + * Authors: + * Xiang Xu <xuxiang@eswincomputing.com> + * Guosheng Wang <wangguosheng@eswincomputing.com> + * Xuyang Dong <dongxuyang@eswincomputing.com> + * + * Characteristics: + * - The hardware can generate 0% and 100% duty cycle. + * - 0% duty cycle (fully off) + * - 100% duty cycle (fully on) + * + * Limitations: + * - The duty cycle and period cannot both be set to 0. + * - The controller hardware provides up to 3 PWM channels. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/pinctrl/consumer.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/pwm.h> +#include <linux/reset.h> + +#include "pwm-dwc.h" + +#define DW_PWMS_TOTAL 3 + +static int eic7700_pwm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dwc_pwm_drvdata *data; + struct pwm_chip *chip = NULL; + struct dwc_pwm *dwc = NULL; + int ret; + + data = devm_kzalloc(dev, sizeof(data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + chip = dwc_pwm_alloc(dev); + if (IS_ERR(chip)) + return dev_err_probe(dev, -ENOMEM, "failed to alloc pwm\n"); + + dwc = to_dwc_pwm(chip); + + if (of_property_read_bool(dev->of_node, "snps,pwm-full-range-enable")) + dwc->pwm_0n100_enable = true; + + dwc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dwc->base)) + return dev_err_probe(dev, PTR_ERR(dwc->base), + "failed to get base\n"); + + dwc->clk = devm_clk_get(dev, NULL); + if (IS_ERR(dwc->clk)) + return dev_err_probe(dev, PTR_ERR(dwc->clk), + "failed to get clock\n"); + + ret = clk_prepare_enable(dwc->clk); + if (ret) + return dev_err_probe(dev, ret, "failed to enable clock\n"); + + dwc->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(dwc->rst)) { + dev_err(dev, "failed to get reset control\n"); + ret = PTR_ERR(dwc->rst); + goto err_clk_disable; + } + + ret = reset_control_deassert(dwc->rst); + if (ret) { + dev_err(dev, "failed to deassert reset: %d\n", ret); + goto err_clk_disable; + } + + ret = devm_pwmchip_add(dev, chip); + if (ret) { + dev_err(dev, "failed to add pwmchip: %d\n", ret); + goto err_reset_assert; + } + + data->chips[0] = chip; + dev_set_drvdata(dev, data); + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_get_noresume(dev); + + return 0; + +err_reset_assert: + reset_control_assert(dwc->rst); +err_clk_disable: + clk_disable_unprepare(dwc->clk); + + return ret; +} + +static void eic7700_pwm_remove(struct platform_device *pdev) +{ + struct dwc_pwm_drvdata *data = platform_get_drvdata(pdev); + struct pwm_chip *chip = data->chips[0]; + struct dwc_pwm *dwc = to_dwc_pwm(chip); + struct device *dev = &pdev->dev; + + clk_disable_unprepare(dwc->clk); + reset_control_assert(dwc->rst); + + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); +} + +static int eic7700_pwm_runtime_suspend(struct device *dev) +{ + struct dwc_pwm_drvdata *data = dev_get_drvdata(dev); + struct pwm_chip *chip = data->chips[0]; + struct dwc_pwm *dwc = to_dwc_pwm(chip); + int idx, ret; + + for (idx = 0; idx < DW_PWMS_TOTAL; idx++) { + if (chip->pwms[idx].state.enabled) + return -EBUSY; + + dwc->ctx[idx].cnt = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT(idx)); + dwc->ctx[idx].cnt2 = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT2(idx)); + dwc->ctx[idx].ctrl = dwc_pwm_readl(dwc, DWC_TIM_CTRL(idx)); + } + + clk_disable_unprepare(dwc->clk); + ret = pinctrl_pm_select_sleep_state(dev); + if (ret) { + dev_err(dev, "failed to select sleep state: %d\n", ret); + clk_prepare_enable(dwc->clk); + return ret; + } + + return 0; +} + +static int eic7700_pwm_runtime_resume(struct device *dev) +{ + struct dwc_pwm_drvdata *data = dev_get_drvdata(dev); + struct pwm_chip *chip = data->chips[0]; + struct dwc_pwm *dwc = to_dwc_pwm(chip); + int idx, ret; + + ret = pinctrl_pm_select_default_state(dev); + if (ret) { + dev_err(dev, "failed to select default state: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(dwc->clk); + if (ret) { + dev_err(dev, "failed to enable clock: %d\n", ret); + return ret; + } + + for (idx = 0; idx < DW_PWMS_TOTAL; idx++) { + dwc_pwm_writel(dwc, dwc->ctx[idx].cnt, DWC_TIM_LD_CNT(idx)); + dwc_pwm_writel(dwc, dwc->ctx[idx].cnt2, DWC_TIM_LD_CNT2(idx)); + dwc_pwm_writel(dwc, dwc->ctx[idx].ctrl, DWC_TIM_CTRL(idx)); + } + + return 0; +} + +static int eic7700_pwm_suspend(struct device *dev) +{ + int ret; + + if (!pm_runtime_suspended(dev)) { + ret = eic7700_pwm_runtime_suspend(dev); + if (ret) + return ret; + } + + return 0; +} + +static int eic7700_pwm_resume(struct device *dev) +{ + int ret; + + if (!pm_runtime_suspended(dev)) { + ret = eic7700_pwm_runtime_resume(dev); + if (ret) + return ret; + } + + return 0; +} + +static const struct dev_pm_ops eic7700_pwm_pm_ops = { + RUNTIME_PM_OPS(eic7700_pwm_runtime_suspend, eic7700_pwm_runtime_resume, + NULL) + SYSTEM_SLEEP_PM_OPS(eic7700_pwm_suspend, eic7700_pwm_resume) +}; + +static const struct of_device_id eic7700_pwm_id_table[] = { + { .compatible = "eswin,eic7700-pwm", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, eic7700_pwm_id_table); + +static struct platform_driver eic7700_pwm_driver = { + .probe = eic7700_pwm_probe, + .remove = eic7700_pwm_remove, + .driver = { + .name = "eic7700-pwm", + .pm = pm_sleep_ptr(&eic7700_pwm_pm_ops), + .of_match_table = of_match_ptr(eic7700_pwm_id_table), + }, +}; + +module_platform_driver(eic7700_pwm_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Xiang Xu <xuxiang@eswincomputing.com>"); +MODULE_AUTHOR("Guosheng Wang <wangguosheng@eswincomputing.com>"); +MODULE_AUTHOR("Xuyang Dong <dongxuyang@eswincomputing.com>"); +MODULE_DESCRIPTION("ESWIN EIC7700 PWM Controller"); diff --git a/drivers/pwm/pwm-dwc.h b/drivers/pwm/pwm-dwc.h index 1562594e7f85..27d567e82ac0 100644 --- a/drivers/pwm/pwm-dwc.h +++ b/drivers/pwm/pwm-dwc.h @@ -26,12 +26,13 @@ MODULE_IMPORT_NS("dwc_pwm"); #define DWC_TIMERS_TOTAL 8 /* Timer Control Register */ -#define DWC_TIM_CTRL_EN BIT(0) -#define DWC_TIM_CTRL_MODE BIT(1) -#define DWC_TIM_CTRL_MODE_FREE (0 << 1) -#define DWC_TIM_CTRL_MODE_USER (1 << 1) -#define DWC_TIM_CTRL_INT_MASK BIT(2) -#define DWC_TIM_CTRL_PWM BIT(3) +#define DWC_TIM_CTRL_EN BIT(0) +#define DWC_TIM_CTRL_MODE BIT(1) +#define DWC_TIM_CTRL_MODE_FREE (0 << 1) +#define DWC_TIM_CTRL_MODE_USER BIT(1) +#define DWC_TIM_CTRL_INT_MASK BIT(2) +#define DWC_TIM_CTRL_PWM BIT(3) +#define DWC_TIM_CTRL_0N100PWM_EN BIT(4) struct dwc_pwm_info { unsigned int nr; @@ -53,6 +54,9 @@ struct dwc_pwm_ctx { struct dwc_pwm { void __iomem *base; unsigned int clk_ns; + struct clk *clk; + struct reset_control *rst; + bool pwm_0n100_enable; struct dwc_pwm_ctx ctx[DWC_TIMERS_TOTAL]; }; -- 2.34.1 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] pwm: eswin: Add EIC7700 pwm driver 2025-12-05 9:05 ` [PATCH 2/2] pwm: eswin: Add EIC7700 pwm driver dongxuyang @ 2025-12-06 10:09 ` kernel test robot 2025-12-08 6:48 ` Krzysztof Kozlowski 1 sibling, 0 replies; 8+ messages in thread From: kernel test robot @ 2025-12-06 10:09 UTC (permalink / raw) To: dongxuyang, ukleinek, robh, krzk+dt, conor+dt, p.zabel, linux-pwm, devicetree, linux-kernel Cc: oe-kbuild-all, ningyu, linmin, xuxiang, wangguosheng, pinkesh.vaghela, Xuyang Dong Hi, kernel test robot noticed the following build warnings: [auto build test WARNING on robh/for-next] [also build test WARNING on linus/master v6.18 next-20251205] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/dongxuyang-eswincomputing-com/dt-bindings-pwm-eswin-Add-EIC7700-pwm-controller/20251205-171328 base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next patch link: https://lore.kernel.org/r/20251205090509.1501-1-dongxuyang%40eswincomputing.com patch subject: [PATCH 2/2] pwm: eswin: Add EIC7700 pwm driver config: m68k-allmodconfig (https://download.01.org/0day-ci/archive/20251206/202512061720.j31AsgM7-lkp@intel.com/config) compiler: m68k-linux-gcc (GCC) 15.1.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251206/202512061720.j31AsgM7-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202512061720.j31AsgM7-lkp@intel.com/ All warnings (new ones prefixed by >>): drivers/pwm/pwm-dwc-core.c: In function '__dwc_pwm_configure_timer': >> drivers/pwm/pwm-dwc-core.c:54:43: warning: left shift count >= width of type [-Wshift-count-overflow] 54 | if (tmp < 0 || tmp > (1UL << 32)) | ^~ drivers/pwm/pwm-dwc-core.c:64:43: warning: left shift count >= width of type [-Wshift-count-overflow] 64 | if (tmp < 0 || tmp > (1UL << 32)) | ^~ vim +54 drivers/pwm/pwm-dwc-core.c 37 38 static int __dwc_pwm_configure_timer(struct dwc_pwm *dwc, 39 struct pwm_device *pwm, 40 const struct pwm_state *state) 41 { 42 u64 tmp; 43 u32 ctrl; 44 u32 high; 45 u32 low; 46 47 if (dwc->pwm_0n100_enable) { 48 /* 49 * Calculate width of low and high period in terms of input 50 * clock periods and check are the result within HW limits 51 * between 0 and 2^32 periods. 52 */ 53 tmp = DIV_ROUND_CLOSEST_ULL(state->duty_cycle, dwc->clk_ns); > 54 if (tmp < 0 || tmp > (1UL << 32)) 55 return -ERANGE; 56 57 if (pwm->args.polarity == PWM_POLARITY_INVERSED) 58 high = tmp; 59 else 60 low = tmp; 61 62 tmp = DIV_ROUND_CLOSEST_ULL(state->period - state->duty_cycle, 63 dwc->clk_ns); 64 if (tmp < 0 || tmp > (1UL << 32)) 65 return -ERANGE; 66 67 if (pwm->args.polarity == PWM_POLARITY_INVERSED) 68 low = tmp; 69 else 70 high = tmp; 71 } else { 72 /* 73 * Calculate width of low and high period in terms of input 74 * clock periods and check are the result within HW limits 75 * between 1 and 2^32 periods. 76 */ 77 tmp = DIV_ROUND_CLOSEST_ULL(state->duty_cycle, dwc->clk_ns); 78 if (tmp < 1 || tmp > (1ULL << 32)) 79 return -ERANGE; 80 low = tmp - 1; 81 82 tmp = DIV_ROUND_CLOSEST_ULL(state->period - state->duty_cycle, 83 dwc->clk_ns); 84 if (tmp < 1 || tmp > (1ULL << 32)) 85 return -ERANGE; 86 high = tmp - 1; 87 } 88 89 /* 90 * Specification says timer usage flow is to disable timer, then 91 * program it followed by enable. It also says Load Count is loaded 92 * into timer after it is enabled - either after a disable or 93 * a reset. Based on measurements it happens also without disable 94 * whenever Load Count is updated. But follow the specification. 95 */ 96 __dwc_pwm_set_enable(dwc, pwm->hwpwm, false); 97 98 /* 99 * Write Load Count and Load Count 2 registers. Former defines the 100 * width of low period and latter the width of high period in terms 101 * multiple of input clock periods: 102 * Width = ((Count + 1) * input clock period) or 103 * Width = (Count * input clock period) : supported 0% and 100%). 104 */ 105 dwc_pwm_writel(dwc, low, DWC_TIM_LD_CNT(pwm->hwpwm)); 106 dwc_pwm_writel(dwc, high, DWC_TIM_LD_CNT2(pwm->hwpwm)); 107 108 /* 109 * Set user-defined mode, timer reloads from Load Count registers 110 * when it counts down to 0. 111 * Set PWM mode, it makes output to toggle and width of low and high 112 * periods are set by Load Count registers. 113 */ 114 ctrl = DWC_TIM_CTRL_MODE_USER | DWC_TIM_CTRL_PWM; 115 if (dwc->pwm_0n100_enable) 116 ctrl |= DWC_TIM_CTRL_0N100PWM_EN; 117 118 dwc_pwm_writel(dwc, ctrl, DWC_TIM_CTRL(pwm->hwpwm)); 119 120 /* 121 * Enable timer. Output starts from low period. 122 */ 123 __dwc_pwm_set_enable(dwc, pwm->hwpwm, state->enabled); 124 125 return 0; 126 } 127 -- 0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/2] pwm: eswin: Add EIC7700 pwm driver 2025-12-05 9:05 ` [PATCH 2/2] pwm: eswin: Add EIC7700 pwm driver dongxuyang 2025-12-06 10:09 ` kernel test robot @ 2025-12-08 6:48 ` Krzysztof Kozlowski 1 sibling, 0 replies; 8+ messages in thread From: Krzysztof Kozlowski @ 2025-12-08 6:48 UTC (permalink / raw) To: dongxuyang Cc: ukleinek, robh, krzk+dt, conor+dt, p.zabel, linux-pwm, devicetree, linux-kernel, ningyu, linmin, xuxiang, wangguosheng, pinkesh.vaghela On Fri, Dec 05, 2025 at 05:05:09PM +0800, dongxuyang@eswincomputing.com wrote: + * > + * Limitations: > + * - The duty cycle and period cannot both be set to 0. > + * - The controller hardware provides up to 3 PWM channels. > + */ > + > +#include <linux/clk.h> > +#include <linux/delay.h> > +#include <linux/err.h> > +#include <linux/io.h> Where do you use half of above headers? > +#include <linux/pinctrl/consumer.h> > +#include <linux/platform_device.h> > +#include <linux/pm_runtime.h> > +#include <linux/pwm.h> > +#include <linux/reset.h> > + > +#include "pwm-dwc.h" > + > +#define DW_PWMS_TOTAL 3 > + > +static int eic7700_pwm_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct dwc_pwm_drvdata *data; > + struct pwm_chip *chip = NULL; > + struct dwc_pwm *dwc = NULL; > + int ret; > + > + data = devm_kzalloc(dev, sizeof(data), GFP_KERNEL); > + if (!data) > + return -ENOMEM; > + > + chip = dwc_pwm_alloc(dev); > + if (IS_ERR(chip)) > + return dev_err_probe(dev, -ENOMEM, "failed to alloc pwm\n"); > + > + dwc = to_dwc_pwm(chip); > + > + if (of_property_read_bool(dev->of_node, "snps,pwm-full-range-enable")) > + dwc->pwm_0n100_enable = true; > + > + dwc->base = devm_platform_ioremap_resource(pdev, 0); > + if (IS_ERR(dwc->base)) > + return dev_err_probe(dev, PTR_ERR(dwc->base), > + "failed to get base\n"); > + > + dwc->clk = devm_clk_get(dev, NULL); Just use the enabled varian and drop below prepare enable call. > + if (IS_ERR(dwc->clk)) > + return dev_err_probe(dev, PTR_ERR(dwc->clk), > + "failed to get clock\n"); > + > + ret = clk_prepare_enable(dwc->clk); > + if (ret) > + return dev_err_probe(dev, ret, "failed to enable clock\n"); > + > + dwc->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); > + if (IS_ERR(dwc->rst)) { > + dev_err(dev, "failed to get reset control\n"); > + ret = PTR_ERR(dwc->rst); > + goto err_clk_disable; > + } > + > + ret = reset_control_deassert(dwc->rst); > + if (ret) { > + dev_err(dev, "failed to deassert reset: %d\n", ret); > + goto err_clk_disable; > + } > + > + ret = devm_pwmchip_add(dev, chip); > + if (ret) { > + dev_err(dev, "failed to add pwmchip: %d\n", ret); > + goto err_reset_assert; > + } > + > + data->chips[0] = chip; > + dev_set_drvdata(dev, data); > + > + pm_runtime_set_active(dev); > + pm_runtime_enable(dev); > + pm_runtime_get_noresume(dev); > + > + return 0; > + > +err_reset_assert: > + reset_control_assert(dwc->rst); > +err_clk_disable: > + clk_disable_unprepare(dwc->clk); > + > + return ret; > +} > + > +static void eic7700_pwm_remove(struct platform_device *pdev) > +{ > + struct dwc_pwm_drvdata *data = platform_get_drvdata(pdev); > + struct pwm_chip *chip = data->chips[0]; > + struct dwc_pwm *dwc = to_dwc_pwm(chip); > + struct device *dev = &pdev->dev; > + > + clk_disable_unprepare(dwc->clk); > + reset_control_assert(dwc->rst); > + > + pm_runtime_put_sync(dev); > + pm_runtime_disable(dev); > +} > + > +static int eic7700_pwm_runtime_suspend(struct device *dev) > +{ > + struct dwc_pwm_drvdata *data = dev_get_drvdata(dev); > + struct pwm_chip *chip = data->chips[0]; > + struct dwc_pwm *dwc = to_dwc_pwm(chip); > + int idx, ret; > + > + for (idx = 0; idx < DW_PWMS_TOTAL; idx++) { > + if (chip->pwms[idx].state.enabled) > + return -EBUSY; > + > + dwc->ctx[idx].cnt = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT(idx)); > + dwc->ctx[idx].cnt2 = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT2(idx)); > + dwc->ctx[idx].ctrl = dwc_pwm_readl(dwc, DWC_TIM_CTRL(idx)); > + } > + > + clk_disable_unprepare(dwc->clk); > + ret = pinctrl_pm_select_sleep_state(dev); > + if (ret) { > + dev_err(dev, "failed to select sleep state: %d\n", ret); > + clk_prepare_enable(dwc->clk); > + return ret; > + } > + > + return 0; > +} > + > +static int eic7700_pwm_runtime_resume(struct device *dev) > +{ > + struct dwc_pwm_drvdata *data = dev_get_drvdata(dev); > + struct pwm_chip *chip = data->chips[0]; > + struct dwc_pwm *dwc = to_dwc_pwm(chip); > + int idx, ret; > + > + ret = pinctrl_pm_select_default_state(dev); > + if (ret) { > + dev_err(dev, "failed to select default state: %d\n", ret); > + return ret; > + } > + > + ret = clk_prepare_enable(dwc->clk); > + if (ret) { > + dev_err(dev, "failed to enable clock: %d\n", ret); > + return ret; > + } > + > + for (idx = 0; idx < DW_PWMS_TOTAL; idx++) { > + dwc_pwm_writel(dwc, dwc->ctx[idx].cnt, DWC_TIM_LD_CNT(idx)); > + dwc_pwm_writel(dwc, dwc->ctx[idx].cnt2, DWC_TIM_LD_CNT2(idx)); > + dwc_pwm_writel(dwc, dwc->ctx[idx].ctrl, DWC_TIM_CTRL(idx)); > + } > + > + return 0; > +} > + > +static int eic7700_pwm_suspend(struct device *dev) > +{ > + int ret; > + > + if (!pm_runtime_suspended(dev)) { > + ret = eic7700_pwm_runtime_suspend(dev); > + if (ret) > + return ret; > + } > + > + return 0; > +} > + > +static int eic7700_pwm_resume(struct device *dev) > +{ > + int ret; > + > + if (!pm_runtime_suspended(dev)) { > + ret = eic7700_pwm_runtime_resume(dev); > + if (ret) > + return ret; > + } > + > + return 0; > +} > + > +static const struct dev_pm_ops eic7700_pwm_pm_ops = { > + RUNTIME_PM_OPS(eic7700_pwm_runtime_suspend, eic7700_pwm_runtime_resume, > + NULL) > + SYSTEM_SLEEP_PM_OPS(eic7700_pwm_suspend, eic7700_pwm_resume) > +}; > + > +static const struct of_device_id eic7700_pwm_id_table[] = { > + { .compatible = "eswin,eic7700-pwm", }, > + { /* sentinel */ } > +}; > +MODULE_DEVICE_TABLE(of, eic7700_pwm_id_table); > + > +static struct platform_driver eic7700_pwm_driver = { > + .probe = eic7700_pwm_probe, > + .remove = eic7700_pwm_remove, > + .driver = { > + .name = "eic7700-pwm", > + .pm = pm_sleep_ptr(&eic7700_pwm_pm_ops), > + .of_match_table = of_match_ptr(eic7700_pwm_id_table), Drop of_match_ptr, you have warning here. Look at other drivers how they do it. Best regards, Krzysztof ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2025-12-09 21:41 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-12-05 9:04 [PATCH 0/2] Add driver support for ESWIN EIC7700 PWM controller dongxuyang 2025-12-05 9:04 ` [PATCH 1/2] dt-bindings: pwm: eswin: Add EIC7700 pwm controller dongxuyang 2025-12-08 6:45 ` Krzysztof Kozlowski 2025-12-09 10:20 ` Xuyang Dong 2025-12-09 21:40 ` Krzysztof Kozlowski 2025-12-05 9:05 ` [PATCH 2/2] pwm: eswin: Add EIC7700 pwm driver dongxuyang 2025-12-06 10:09 ` kernel test robot 2025-12-08 6:48 ` Krzysztof Kozlowski
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox