* [PATCH v12 1/5] riscv: dts: sifive: unleashed/unmatched: Remove PWM controlled LED's active-low properties
2025-04-22 8:53 [PATCH v12 0/5] Change PWM-controlled LED pin active mode and algorithm Nylon Chen
@ 2025-04-22 8:53 ` Nylon Chen
2025-04-22 8:53 ` [PATCH v12 2/5] pwm: sifive: change the PWM algorithm Nylon Chen
` (3 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Nylon Chen @ 2025-04-22 8:53 UTC (permalink / raw)
To: Conor Dooley, Rob Herring, Krzysztof Kozlowski, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Samuel Holland, Uwe Kleine-König
Cc: linux-riscv, devicetree, linux-kernel, linux-pwm, Nylon Chen,
Conor Dooley, Vincent Chen
This removes the active-low properties of the PWM-controlled LEDs in
the HiFive Unmatched device tree.
The reference is hifive-unleashed-a00.pdf[0] and hifive-unmatched-schematics-v3.pdf[1].
Link: https://sifive.cdn.prismic.io/sifive/c52a8e32-05ce-4aaf-95c8-7bf8453f8698_hifive-unleashed-a00-schematics-1.pdf [0]
Link: https://sifive.cdn.prismic.io/sifive/6a06d6c0-6e66-49b5-8e9e-e68ce76f4192_hifive-unmatched-schematics-v3.pdf [1]
Acked-by: Conor Dooley <conor.dooley@microchip.com>
Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Vincent Chen <vincent.chen@sifive.com>
Signed-off-by: Nylon Chen <nylon.chen@sifive.com>
---
arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts | 12 ++++--------
arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts | 12 ++++--------
2 files changed, 8 insertions(+), 16 deletions(-)
diff --git a/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts
index 900a50526d77..06731b8c7bc3 100644
--- a/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts
+++ b/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts
@@ -49,32 +49,28 @@ led-controller {
compatible = "pwm-leds";
led-d1 {
- pwms = <&pwm0 0 7812500 PWM_POLARITY_INVERTED>;
- active-low;
+ pwms = <&pwm0 0 7812500 0>;
color = <LED_COLOR_ID_GREEN>;
max-brightness = <255>;
label = "d1";
};
led-d2 {
- pwms = <&pwm0 1 7812500 PWM_POLARITY_INVERTED>;
- active-low;
+ pwms = <&pwm0 1 7812500 0>;
color = <LED_COLOR_ID_GREEN>;
max-brightness = <255>;
label = "d2";
};
led-d3 {
- pwms = <&pwm0 2 7812500 PWM_POLARITY_INVERTED>;
- active-low;
+ pwms = <&pwm0 2 7812500 0>;
color = <LED_COLOR_ID_GREEN>;
max-brightness = <255>;
label = "d3";
};
led-d4 {
- pwms = <&pwm0 3 7812500 PWM_POLARITY_INVERTED>;
- active-low;
+ pwms = <&pwm0 3 7812500 0>;
color = <LED_COLOR_ID_GREEN>;
max-brightness = <255>;
label = "d4";
diff --git a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
index 72b87b08ab44..03ce2cee4e97 100644
--- a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+++ b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
@@ -51,8 +51,7 @@ led-controller-1 {
compatible = "pwm-leds";
led-d12 {
- pwms = <&pwm0 0 7812500 PWM_POLARITY_INVERTED>;
- active-low;
+ pwms = <&pwm0 0 7812500 0>;
color = <LED_COLOR_ID_GREEN>;
max-brightness = <255>;
label = "d12";
@@ -68,20 +67,17 @@ multi-led {
label = "d2";
led-red {
- pwms = <&pwm0 2 7812500 PWM_POLARITY_INVERTED>;
- active-low;
+ pwms = <&pwm0 2 7812500 0>;
color = <LED_COLOR_ID_RED>;
};
led-green {
- pwms = <&pwm0 1 7812500 PWM_POLARITY_INVERTED>;
- active-low;
+ pwms = <&pwm0 1 7812500 0>;
color = <LED_COLOR_ID_GREEN>;
};
led-blue {
- pwms = <&pwm0 3 7812500 PWM_POLARITY_INVERTED>;
- active-low;
+ pwms = <&pwm0 3 7812500 0>;
color = <LED_COLOR_ID_BLUE>;
};
};
--
2.34.1
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH v12 2/5] pwm: sifive: change the PWM algorithm
2025-04-22 8:53 [PATCH v12 0/5] Change PWM-controlled LED pin active mode and algorithm Nylon Chen
2025-04-22 8:53 ` [PATCH v12 1/5] riscv: dts: sifive: unleashed/unmatched: Remove PWM controlled LED's active-low properties Nylon Chen
@ 2025-04-22 8:53 ` Nylon Chen
2025-04-22 8:53 ` [PATCH v12 3/5] pwm: sifive: Fix the error in the idempotent test within the pwm_apply_state_debug function Nylon Chen
` (2 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Nylon Chen @ 2025-04-22 8:53 UTC (permalink / raw)
To: Conor Dooley, Rob Herring, Krzysztof Kozlowski, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Samuel Holland, Uwe Kleine-König
Cc: linux-riscv, devicetree, linux-kernel, linux-pwm, Nylon Chen,
Zong Li, Vincent Chen
The `frac` variable represents the pulse inactive time, and the result
of this algorithm is the pulse active time. Therefore, we must reverse the result.
The reference is SiFive FU740-C000 Manual[0]
Link: https://sifive.cdn.prismic.io/sifive/1a82e600-1f93-4f41-b2d8-86ed8b16acba_fu740-c000-manual-v1p6.pdf [0]
Co-developed-by: Zong Li <zong.li@sifive.com>
Signed-off-by: Zong Li <zong.li@sifive.com>
Co-developed-by: Vincent Chen <vincent.chen@sifive.com>
Signed-off-by: Vincent Chen <vincent.chen@sifive.com>
Signed-off-by: Nylon Chen <nylon.chen@sifive.com>
---
drivers/pwm/pwm-sifive.c | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c
index d5b647e6be78..bb9146267bc5 100644
--- a/drivers/pwm/pwm-sifive.c
+++ b/drivers/pwm/pwm-sifive.c
@@ -110,9 +110,10 @@ static int pwm_sifive_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_state *state)
{
struct pwm_sifive_ddata *ddata = pwm_sifive_chip_to_ddata(chip);
- u32 duty, val;
+ u32 duty, val, inactive;
- duty = readl(ddata->regs + PWM_SIFIVE_PWMCMP(pwm->hwpwm));
+ inactive = readl(ddata->regs + PWM_SIFIVE_PWMCMP(pwm->hwpwm));
+ duty = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - inactive;
state->enabled = duty > 0;
@@ -123,7 +124,7 @@ static int pwm_sifive_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
state->period = ddata->real_period;
state->duty_cycle =
(u64)duty * ddata->real_period >> PWM_SIFIVE_CMPWIDTH;
- state->polarity = PWM_POLARITY_INVERSED;
+ state->polarity = PWM_POLARITY_NORMAL;
return 0;
}
@@ -137,9 +138,9 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm,
unsigned long long num;
bool enabled;
int ret = 0;
- u32 frac;
+ u32 frac, inactive;
- if (state->polarity != PWM_POLARITY_INVERSED)
+ if (state->polarity != PWM_POLARITY_NORMAL)
return -EINVAL;
cur_state = pwm->state;
@@ -157,8 +158,9 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm,
*/
num = (u64)duty_cycle * (1U << PWM_SIFIVE_CMPWIDTH);
frac = DIV64_U64_ROUND_CLOSEST(num, state->period);
- /* The hardware cannot generate a 100% duty cycle */
+ /* The hardware cannot generate a 0% duty cycle */
frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1);
+ inactive = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - frac;
mutex_lock(&ddata->lock);
if (state->period != ddata->approx_period) {
@@ -190,7 +192,7 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm,
}
}
- writel(frac, ddata->regs + PWM_SIFIVE_PWMCMP(pwm->hwpwm));
+ writel(inactive, ddata->regs + PWM_SIFIVE_PWMCMP(pwm->hwpwm));
if (!state->enabled)
clk_disable(ddata->clk);
--
2.34.1
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH v12 3/5] pwm: sifive: Fix the error in the idempotent test within the pwm_apply_state_debug function
2025-04-22 8:53 [PATCH v12 0/5] Change PWM-controlled LED pin active mode and algorithm Nylon Chen
2025-04-22 8:53 ` [PATCH v12 1/5] riscv: dts: sifive: unleashed/unmatched: Remove PWM controlled LED's active-low properties Nylon Chen
2025-04-22 8:53 ` [PATCH v12 2/5] pwm: sifive: change the PWM algorithm Nylon Chen
@ 2025-04-22 8:53 ` Nylon Chen
2025-04-22 8:53 ` [PATCH v12 4/5] pwm: sifive: Fix rounding issues in apply and get_state functions Nylon Chen
2025-04-22 8:53 ` [PATCH v12 5/5] pwm: sifive: clarify inverted compare logic in comments Nylon Chen
4 siblings, 0 replies; 8+ messages in thread
From: Nylon Chen @ 2025-04-22 8:53 UTC (permalink / raw)
To: Conor Dooley, Rob Herring, Krzysztof Kozlowski, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Samuel Holland, Uwe Kleine-König
Cc: linux-riscv, devicetree, linux-kernel, linux-pwm, Nylon Chen, Zong Li
Round the result to the nearest whole number. This ensures that real_period
is always a reasonable integer that is not lower than the actual value.
e.g.
$ echo 110 > /sys/devices/platform/led-controller-1/leds/d12/brightness
$ .apply is not idempotent (ena=1 pol=0 1739692/4032985) -> (ena=1 pol=0
1739630/4032985)
Co-developed-by: Zong Li <zong.li@sifive.com>
Signed-off-by: Zong Li <zong.li@sifive.com>
Signed-off-by: Nylon Chen <nylon.chen@sifive.com>
---
drivers/pwm/pwm-sifive.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c
index bb9146267bc5..6259f8500f71 100644
--- a/drivers/pwm/pwm-sifive.c
+++ b/drivers/pwm/pwm-sifive.c
@@ -101,7 +101,7 @@ static void pwm_sifive_update_clock(struct pwm_sifive_ddata *ddata,
/* As scale <= 15 the shift operation cannot overflow. */
num = (unsigned long long)NSEC_PER_SEC << (PWM_SIFIVE_CMPWIDTH + scale);
- ddata->real_period = div64_ul(num, rate);
+ ddata->real_period = DIV_ROUND_UP_ULL(num, rate);
dev_dbg(ddata->parent,
"New real_period = %u ns\n", ddata->real_period);
}
--
2.34.1
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v12 4/5] pwm: sifive: Fix rounding issues in apply and get_state functions
2025-04-22 8:53 [PATCH v12 0/5] Change PWM-controlled LED pin active mode and algorithm Nylon Chen
` (2 preceding siblings ...)
2025-04-22 8:53 ` [PATCH v12 3/5] pwm: sifive: Fix the error in the idempotent test within the pwm_apply_state_debug function Nylon Chen
@ 2025-04-22 8:53 ` Nylon Chen
2025-04-23 1:34 ` kernel test robot
2025-04-23 4:08 ` kernel test robot
2025-04-22 8:53 ` [PATCH v12 5/5] pwm: sifive: clarify inverted compare logic in comments Nylon Chen
4 siblings, 2 replies; 8+ messages in thread
From: Nylon Chen @ 2025-04-22 8:53 UTC (permalink / raw)
To: Conor Dooley, Rob Herring, Krzysztof Kozlowski, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Samuel Holland, Uwe Kleine-König
Cc: linux-riscv, devicetree, linux-kernel, linux-pwm, Nylon Chen, Zong Li
Fix PWM apply and get_state rounding to ensure consistency between
setting and reading values
This fixes the reported errors:
pwm-sifive 10021000.pwm: .apply is supposed to round down
duty_cycle (requested: 360/504000, applied: 361/504124)
pwm-sifive 10021000.pwm: .apply is supposed to round down
period (requested: 504000, applied: 504124)
Co-developed-by: Zong Li <zong.li@sifive.com>
Signed-off-by: Zong Li <zong.li@sifive.com>
Signed-off-by: Nylon Chen <nylon.chen@sifive.com>
---
drivers/pwm/pwm-sifive.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c
index 6259f8500f71..065d6d2b84bc 100644
--- a/drivers/pwm/pwm-sifive.c
+++ b/drivers/pwm/pwm-sifive.c
@@ -122,8 +122,8 @@ static int pwm_sifive_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
state->enabled = false;
state->period = ddata->real_period;
- state->duty_cycle =
- (u64)duty * ddata->real_period >> PWM_SIFIVE_CMPWIDTH;
+ state->duty_cycle = DIV_ROUND_UP_ULL((u64)duty * ddata->real_period,
+ (1U << PWM_SIFIVE_CMPWIDTH));
state->polarity = PWM_POLARITY_NORMAL;
return 0;
@@ -157,7 +157,8 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm,
* consecutively
*/
num = (u64)duty_cycle * (1U << PWM_SIFIVE_CMPWIDTH);
- frac = DIV64_U64_ROUND_CLOSEST(num, state->period);
+ frac = num;
+ do_div(frac, state->period;
/* The hardware cannot generate a 0% duty cycle */
frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1);
inactive = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - frac;
--
2.34.1
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH v12 4/5] pwm: sifive: Fix rounding issues in apply and get_state functions
2025-04-22 8:53 ` [PATCH v12 4/5] pwm: sifive: Fix rounding issues in apply and get_state functions Nylon Chen
@ 2025-04-23 1:34 ` kernel test robot
2025-04-23 4:08 ` kernel test robot
1 sibling, 0 replies; 8+ messages in thread
From: kernel test robot @ 2025-04-23 1:34 UTC (permalink / raw)
To: Nylon Chen, Conor Dooley, Rob Herring, Krzysztof Kozlowski,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Samuel Holland,
Uwe Kleine-König
Cc: oe-kbuild-all, linux-riscv, devicetree, linux-kernel, linux-pwm,
Nylon Chen, Zong Li
Hi Nylon,
kernel test robot noticed the following build errors:
[auto build test ERROR on robh/for-next]
[also build test ERROR on linus/master v6.15-rc3 next-20250422]
[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/Nylon-Chen/riscv-dts-sifive-unleashed-unmatched-Remove-PWM-controlled-LED-s-active-low-properties/20250422-165644
base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
patch link: https://lore.kernel.org/r/20250422085312.812877-5-nylon.chen%40sifive.com
patch subject: [PATCH v12 4/5] pwm: sifive: Fix rounding issues in apply and get_state functions
config: csky-randconfig-001-20250423 (https://download.01.org/0day-ci/archive/20250423/202504230904.kXJdvywR-lkp@intel.com/config)
compiler: csky-linux-gcc (GCC) 13.3.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250423/202504230904.kXJdvywR-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/202504230904.kXJdvywR-lkp@intel.com/
All error/warnings (new ones prefixed by >>):
drivers/pwm/pwm-sifive.c: In function 'pwm_sifive_apply':
>> drivers/pwm/pwm-sifive.c:351:26: error: unterminated argument list invoking macro "do_div"
351 | MODULE_LICENSE("GPL v2");
| ^
>> drivers/pwm/pwm-sifive.c:161:9: error: 'do_div' undeclared (first use in this function)
161 | do_div(frac, state->period;
| ^~~~~~
drivers/pwm/pwm-sifive.c:161:9: note: each undeclared identifier is reported only once for each function it appears in
>> drivers/pwm/pwm-sifive.c:161:15: error: expected ';' at end of input
161 | do_div(frac, state->period;
| ^
| ;
......
>> drivers/pwm/pwm-sifive.c:161:9: error: expected declaration or statement at end of input
161 | do_div(frac, state->period;
| ^~~~~~
>> drivers/pwm/pwm-sifive.c:141:19: warning: unused variable 'inactive' [-Wunused-variable]
141 | u32 frac, inactive;
| ^~~~~~~~
>> drivers/pwm/pwm-sifive.c:141:13: warning: variable 'frac' set but not used [-Wunused-but-set-variable]
141 | u32 frac, inactive;
| ^~~~
>> drivers/pwm/pwm-sifive.c:140:13: warning: unused variable 'ret' [-Wunused-variable]
140 | int ret = 0;
| ^~~
>> drivers/pwm/pwm-sifive.c:139:14: warning: variable 'enabled' set but not used [-Wunused-but-set-variable]
139 | bool enabled;
| ^~~~~~~
>> drivers/pwm/pwm-sifive.c:135:34: warning: unused variable 'ddata' [-Wunused-variable]
135 | struct pwm_sifive_ddata *ddata = pwm_sifive_chip_to_ddata(chip);
| ^~~~~
drivers/pwm/pwm-sifive.c: At top level:
>> drivers/pwm/pwm-sifive.c:132:12: warning: 'pwm_sifive_apply' defined but not used [-Wunused-function]
132 | static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm,
| ^~~~~~~~~~~~~~~~
>> drivers/pwm/pwm-sifive.c:109:12: warning: 'pwm_sifive_get_state' defined but not used [-Wunused-function]
109 | static int pwm_sifive_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
| ^~~~~~~~~~~~~~~~~~~~
>> drivers/pwm/pwm-sifive.c:81:13: warning: 'pwm_sifive_update_clock' defined but not used [-Wunused-function]
81 | static void pwm_sifive_update_clock(struct pwm_sifive_ddata *ddata,
| ^~~~~~~~~~~~~~~~~~~~~~~
>> drivers/pwm/pwm-sifive.c:71:13: warning: 'pwm_sifive_free' defined but not used [-Wunused-function]
71 | static void pwm_sifive_free(struct pwm_chip *chip, struct pwm_device *pwm)
| ^~~~~~~~~~~~~~~
>> drivers/pwm/pwm-sifive.c:60:12: warning: 'pwm_sifive_request' defined but not used [-Wunused-function]
60 | static int pwm_sifive_request(struct pwm_chip *chip, struct pwm_device *pwm)
| ^~~~~~~~~~~~~~~~~~
vim +/do_div +351 drivers/pwm/pwm-sifive.c
9e37a53eb05114b Yash Shah 2019-06-11 131
9e37a53eb05114b Yash Shah 2019-06-11 132 static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm,
71523d1812aca61 Uwe Kleine-König 2019-08-24 133 const struct pwm_state *state)
9e37a53eb05114b Yash Shah 2019-06-11 134 {
9e37a53eb05114b Yash Shah 2019-06-11 135 struct pwm_sifive_ddata *ddata = pwm_sifive_chip_to_ddata(chip);
9e37a53eb05114b Yash Shah 2019-06-11 136 struct pwm_state cur_state;
9e37a53eb05114b Yash Shah 2019-06-11 137 unsigned int duty_cycle;
9e37a53eb05114b Yash Shah 2019-06-11 138 unsigned long long num;
9e37a53eb05114b Yash Shah 2019-06-11 139 bool enabled;
9e37a53eb05114b Yash Shah 2019-06-11 140 int ret = 0;
5bb4f54a7d6753d Nylon Chen 2025-04-22 141 u32 frac, inactive;
9e37a53eb05114b Yash Shah 2019-06-11 142
5bb4f54a7d6753d Nylon Chen 2025-04-22 143 if (state->polarity != PWM_POLARITY_NORMAL)
9e37a53eb05114b Yash Shah 2019-06-11 144 return -EINVAL;
9e37a53eb05114b Yash Shah 2019-06-11 145
9e37a53eb05114b Yash Shah 2019-06-11 146 cur_state = pwm->state;
9e37a53eb05114b Yash Shah 2019-06-11 147 enabled = cur_state.enabled;
9e37a53eb05114b Yash Shah 2019-06-11 148
9e37a53eb05114b Yash Shah 2019-06-11 149 duty_cycle = state->duty_cycle;
9e37a53eb05114b Yash Shah 2019-06-11 150 if (!state->enabled)
9e37a53eb05114b Yash Shah 2019-06-11 151 duty_cycle = 0;
9e37a53eb05114b Yash Shah 2019-06-11 152
9e37a53eb05114b Yash Shah 2019-06-11 153 /*
9e37a53eb05114b Yash Shah 2019-06-11 154 * The problem of output producing mixed setting as mentioned at top,
9e37a53eb05114b Yash Shah 2019-06-11 155 * occurs here. To minimize the window for this problem, we are
9e37a53eb05114b Yash Shah 2019-06-11 156 * calculating the register values first and then writing them
9e37a53eb05114b Yash Shah 2019-06-11 157 * consecutively
9e37a53eb05114b Yash Shah 2019-06-11 158 */
9e37a53eb05114b Yash Shah 2019-06-11 159 num = (u64)duty_cycle * (1U << PWM_SIFIVE_CMPWIDTH);
357da405567b634 Nylon Chen 2025-04-22 160 frac = num;
357da405567b634 Nylon Chen 2025-04-22 @161 do_div(frac, state->period;
5bb4f54a7d6753d Nylon Chen 2025-04-22 162 /* The hardware cannot generate a 0% duty cycle */
9e37a53eb05114b Yash Shah 2019-06-11 163 frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1);
5bb4f54a7d6753d Nylon Chen 2025-04-22 164 inactive = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - frac;
9e37a53eb05114b Yash Shah 2019-06-11 165
0f02f491b786143 Uwe Kleine-König 2022-07-21 166 mutex_lock(&ddata->lock);
9e37a53eb05114b Yash Shah 2019-06-11 167 if (state->period != ddata->approx_period) {
334c7b13d38321e Emil Renner Berthing 2022-11-09 168 /*
334c7b13d38321e Emil Renner Berthing 2022-11-09 169 * Don't let a 2nd user change the period underneath the 1st user.
334c7b13d38321e Emil Renner Berthing 2022-11-09 170 * However if ddate->approx_period == 0 this is the first time we set
334c7b13d38321e Emil Renner Berthing 2022-11-09 171 * any period, so let whoever gets here first set the period so other
334c7b13d38321e Emil Renner Berthing 2022-11-09 172 * users who agree on the period won't fail.
334c7b13d38321e Emil Renner Berthing 2022-11-09 173 */
334c7b13d38321e Emil Renner Berthing 2022-11-09 174 if (ddata->user_count != 1 && ddata->approx_period) {
0f02f491b786143 Uwe Kleine-König 2022-07-21 175 mutex_unlock(&ddata->lock);
3586b02663f098a Uwe Kleine-König 2022-07-21 176 return -EBUSY;
9e37a53eb05114b Yash Shah 2019-06-11 177 }
9e37a53eb05114b Yash Shah 2019-06-11 178 ddata->approx_period = state->period;
9e37a53eb05114b Yash Shah 2019-06-11 179 pwm_sifive_update_clock(ddata, clk_get_rate(ddata->clk));
9e37a53eb05114b Yash Shah 2019-06-11 180 }
0f02f491b786143 Uwe Kleine-König 2022-07-21 181 mutex_unlock(&ddata->lock);
9e37a53eb05114b Yash Shah 2019-06-11 182
1695b421e1b587c Uwe Kleine-König 2022-07-21 183 /*
1695b421e1b587c Uwe Kleine-König 2022-07-21 184 * If the PWM is enabled the clk is already on. So only enable it
1695b421e1b587c Uwe Kleine-König 2022-07-21 185 * conditionally to have it on exactly once afterwards independent of
1695b421e1b587c Uwe Kleine-König 2022-07-21 186 * the PWM state.
1695b421e1b587c Uwe Kleine-König 2022-07-21 187 */
1695b421e1b587c Uwe Kleine-König 2022-07-21 188 if (!enabled) {
3586b02663f098a Uwe Kleine-König 2022-07-21 189 ret = clk_enable(ddata->clk);
3586b02663f098a Uwe Kleine-König 2022-07-21 190 if (ret) {
c4f4af7d792c74d Uwe Kleine-König 2024-02-14 191 dev_err(pwmchip_parent(chip), "Enable clk failed\n");
3586b02663f098a Uwe Kleine-König 2022-07-21 192 return ret;
3586b02663f098a Uwe Kleine-König 2022-07-21 193 }
1695b421e1b587c Uwe Kleine-König 2022-07-21 194 }
3586b02663f098a Uwe Kleine-König 2022-07-21 195
5bb4f54a7d6753d Nylon Chen 2025-04-22 196 writel(inactive, ddata->regs + PWM_SIFIVE_PWMCMP(pwm->hwpwm));
9e37a53eb05114b Yash Shah 2019-06-11 197
1695b421e1b587c Uwe Kleine-König 2022-07-21 198 if (!state->enabled)
61180f68ad5b905 Uwe Kleine-König 2022-07-21 199 clk_disable(ddata->clk);
9e37a53eb05114b Yash Shah 2019-06-11 200
3586b02663f098a Uwe Kleine-König 2022-07-21 201 return 0;
9e37a53eb05114b Yash Shah 2019-06-11 202 }
9e37a53eb05114b Yash Shah 2019-06-11 203
9e37a53eb05114b Yash Shah 2019-06-11 204 static const struct pwm_ops pwm_sifive_ops = {
9e37a53eb05114b Yash Shah 2019-06-11 205 .request = pwm_sifive_request,
9e37a53eb05114b Yash Shah 2019-06-11 206 .free = pwm_sifive_free,
9e37a53eb05114b Yash Shah 2019-06-11 207 .get_state = pwm_sifive_get_state,
9e37a53eb05114b Yash Shah 2019-06-11 208 .apply = pwm_sifive_apply,
9e37a53eb05114b Yash Shah 2019-06-11 209 };
9e37a53eb05114b Yash Shah 2019-06-11 210
9e37a53eb05114b Yash Shah 2019-06-11 211 static int pwm_sifive_clock_notifier(struct notifier_block *nb,
9e37a53eb05114b Yash Shah 2019-06-11 212 unsigned long event, void *data)
9e37a53eb05114b Yash Shah 2019-06-11 213 {
9e37a53eb05114b Yash Shah 2019-06-11 214 struct clk_notifier_data *ndata = data;
9e37a53eb05114b Yash Shah 2019-06-11 215 struct pwm_sifive_ddata *ddata =
9e37a53eb05114b Yash Shah 2019-06-11 216 container_of(nb, struct pwm_sifive_ddata, notifier);
9e37a53eb05114b Yash Shah 2019-06-11 217
45558b3abb87eeb Uwe Kleine-König 2022-12-02 218 if (event == POST_RATE_CHANGE) {
45558b3abb87eeb Uwe Kleine-König 2022-12-02 219 mutex_lock(&ddata->lock);
9e37a53eb05114b Yash Shah 2019-06-11 220 pwm_sifive_update_clock(ddata, ndata->new_rate);
45558b3abb87eeb Uwe Kleine-König 2022-12-02 221 mutex_unlock(&ddata->lock);
45558b3abb87eeb Uwe Kleine-König 2022-12-02 222 }
9e37a53eb05114b Yash Shah 2019-06-11 223
9e37a53eb05114b Yash Shah 2019-06-11 224 return NOTIFY_OK;
9e37a53eb05114b Yash Shah 2019-06-11 225 }
9e37a53eb05114b Yash Shah 2019-06-11 226
9e37a53eb05114b Yash Shah 2019-06-11 227 static int pwm_sifive_probe(struct platform_device *pdev)
9e37a53eb05114b Yash Shah 2019-06-11 228 {
9e37a53eb05114b Yash Shah 2019-06-11 229 struct device *dev = &pdev->dev;
9e37a53eb05114b Yash Shah 2019-06-11 230 struct pwm_sifive_ddata *ddata;
9e37a53eb05114b Yash Shah 2019-06-11 231 struct pwm_chip *chip;
9e37a53eb05114b Yash Shah 2019-06-11 232 int ret;
ace41d7564e655c Uwe Kleine-König 2022-07-21 233 u32 val;
ace41d7564e655c Uwe Kleine-König 2022-07-21 234 unsigned int enabled_pwms = 0, enabled_clks = 1;
9e37a53eb05114b Yash Shah 2019-06-11 235
554d9acae42b3ee Uwe Kleine-König 2024-02-14 236 chip = devm_pwmchip_alloc(dev, 4, sizeof(*ddata));
554d9acae42b3ee Uwe Kleine-König 2024-02-14 237 if (IS_ERR(chip))
554d9acae42b3ee Uwe Kleine-König 2024-02-14 238 return PTR_ERR(chip);
9e37a53eb05114b Yash Shah 2019-06-11 239
554d9acae42b3ee Uwe Kleine-König 2024-02-14 240 ddata = pwm_sifive_chip_to_ddata(chip);
554d9acae42b3ee Uwe Kleine-König 2024-02-14 241 ddata->parent = dev;
9e37a53eb05114b Yash Shah 2019-06-11 242 mutex_init(&ddata->lock);
9e37a53eb05114b Yash Shah 2019-06-11 243 chip->ops = &pwm_sifive_ops;
9e37a53eb05114b Yash Shah 2019-06-11 244
96cfceba3967198 Yangtao Li 2019-12-29 245 ddata->regs = devm_platform_ioremap_resource(pdev, 0);
f6abac0379b8368 Ding Xiang 2019-07-18 246 if (IS_ERR(ddata->regs))
9e37a53eb05114b Yash Shah 2019-06-11 247 return PTR_ERR(ddata->regs);
9e37a53eb05114b Yash Shah 2019-06-11 248
55e644b840baf7a Uwe Kleine-König 2023-04-18 249 ddata->clk = devm_clk_get_prepared(dev, NULL);
5530fcaf9ca30b8 Krzysztof Kozlowski 2020-08-26 250 if (IS_ERR(ddata->clk))
5530fcaf9ca30b8 Krzysztof Kozlowski 2020-08-26 251 return dev_err_probe(dev, PTR_ERR(ddata->clk),
5530fcaf9ca30b8 Krzysztof Kozlowski 2020-08-26 252 "Unable to find controller clock\n");
9e37a53eb05114b Yash Shah 2019-06-11 253
55e644b840baf7a Uwe Kleine-König 2023-04-18 254 ret = clk_enable(ddata->clk);
9e37a53eb05114b Yash Shah 2019-06-11 255 if (ret) {
9e37a53eb05114b Yash Shah 2019-06-11 256 dev_err(dev, "failed to enable clock for pwm: %d\n", ret);
9e37a53eb05114b Yash Shah 2019-06-11 257 return ret;
9e37a53eb05114b Yash Shah 2019-06-11 258 }
9e37a53eb05114b Yash Shah 2019-06-11 259
ace41d7564e655c Uwe Kleine-König 2022-07-21 260 val = readl(ddata->regs + PWM_SIFIVE_PWMCFG);
ace41d7564e655c Uwe Kleine-König 2022-07-21 261 if (val & PWM_SIFIVE_PWMCFG_EN_ALWAYS) {
ace41d7564e655c Uwe Kleine-König 2022-07-21 262 unsigned int i;
ace41d7564e655c Uwe Kleine-König 2022-07-21 263
ace41d7564e655c Uwe Kleine-König 2022-07-21 264 for (i = 0; i < chip->npwm; ++i) {
ace41d7564e655c Uwe Kleine-König 2022-07-21 265 val = readl(ddata->regs + PWM_SIFIVE_PWMCMP(i));
ace41d7564e655c Uwe Kleine-König 2022-07-21 266 if (val > 0)
ace41d7564e655c Uwe Kleine-König 2022-07-21 267 ++enabled_pwms;
ace41d7564e655c Uwe Kleine-König 2022-07-21 268 }
ace41d7564e655c Uwe Kleine-König 2022-07-21 269 }
ace41d7564e655c Uwe Kleine-König 2022-07-21 270
ace41d7564e655c Uwe Kleine-König 2022-07-21 271 /* The clk should be on once for each running PWM. */
ace41d7564e655c Uwe Kleine-König 2022-07-21 272 if (enabled_pwms) {
ace41d7564e655c Uwe Kleine-König 2022-07-21 273 while (enabled_clks < enabled_pwms) {
ace41d7564e655c Uwe Kleine-König 2022-07-21 274 /* This is not expected to fail as the clk is already on */
ace41d7564e655c Uwe Kleine-König 2022-07-21 275 ret = clk_enable(ddata->clk);
ace41d7564e655c Uwe Kleine-König 2022-07-21 276 if (unlikely(ret)) {
ace41d7564e655c Uwe Kleine-König 2022-07-21 277 dev_err_probe(dev, ret, "Failed to enable clk\n");
ace41d7564e655c Uwe Kleine-König 2022-07-21 278 goto disable_clk;
ace41d7564e655c Uwe Kleine-König 2022-07-21 279 }
ace41d7564e655c Uwe Kleine-König 2022-07-21 280 ++enabled_clks;
ace41d7564e655c Uwe Kleine-König 2022-07-21 281 }
ace41d7564e655c Uwe Kleine-König 2022-07-21 282 } else {
ace41d7564e655c Uwe Kleine-König 2022-07-21 283 clk_disable(ddata->clk);
ace41d7564e655c Uwe Kleine-König 2022-07-21 284 enabled_clks = 0;
ace41d7564e655c Uwe Kleine-König 2022-07-21 285 }
ace41d7564e655c Uwe Kleine-König 2022-07-21 286
9e37a53eb05114b Yash Shah 2019-06-11 287 /* Watch for changes to underlying clock frequency */
9e37a53eb05114b Yash Shah 2019-06-11 288 ddata->notifier.notifier_call = pwm_sifive_clock_notifier;
9e37a53eb05114b Yash Shah 2019-06-11 289 ret = clk_notifier_register(ddata->clk, &ddata->notifier);
9e37a53eb05114b Yash Shah 2019-06-11 290 if (ret) {
9e37a53eb05114b Yash Shah 2019-06-11 291 dev_err(dev, "failed to register clock notifier: %d\n", ret);
9e37a53eb05114b Yash Shah 2019-06-11 292 goto disable_clk;
9e37a53eb05114b Yash Shah 2019-06-11 293 }
9e37a53eb05114b Yash Shah 2019-06-11 294
9e37a53eb05114b Yash Shah 2019-06-11 295 ret = pwmchip_add(chip);
9e37a53eb05114b Yash Shah 2019-06-11 296 if (ret < 0) {
9e37a53eb05114b Yash Shah 2019-06-11 297 dev_err(dev, "cannot register PWM: %d\n", ret);
9e37a53eb05114b Yash Shah 2019-06-11 298 goto unregister_clk;
9e37a53eb05114b Yash Shah 2019-06-11 299 }
9e37a53eb05114b Yash Shah 2019-06-11 300
bb472da2148f39b Uwe Kleine-König 2024-02-14 301 platform_set_drvdata(pdev, chip);
9e37a53eb05114b Yash Shah 2019-06-11 302 dev_dbg(dev, "SiFive PWM chip registered %d PWMs\n", chip->npwm);
9e37a53eb05114b Yash Shah 2019-06-11 303
9e37a53eb05114b Yash Shah 2019-06-11 304 return 0;
9e37a53eb05114b Yash Shah 2019-06-11 305
9e37a53eb05114b Yash Shah 2019-06-11 306 unregister_clk:
9e37a53eb05114b Yash Shah 2019-06-11 307 clk_notifier_unregister(ddata->clk, &ddata->notifier);
9e37a53eb05114b Yash Shah 2019-06-11 308 disable_clk:
ace41d7564e655c Uwe Kleine-König 2022-07-21 309 while (enabled_clks) {
ace41d7564e655c Uwe Kleine-König 2022-07-21 310 clk_disable(ddata->clk);
ace41d7564e655c Uwe Kleine-König 2022-07-21 311 --enabled_clks;
ace41d7564e655c Uwe Kleine-König 2022-07-21 312 }
9e37a53eb05114b Yash Shah 2019-06-11 313
9e37a53eb05114b Yash Shah 2019-06-11 314 return ret;
9e37a53eb05114b Yash Shah 2019-06-11 315 }
9e37a53eb05114b Yash Shah 2019-06-11 316
533d29471b3c940 Uwe Kleine-König 2023-03-03 317 static void pwm_sifive_remove(struct platform_device *dev)
9e37a53eb05114b Yash Shah 2019-06-11 318 {
bb472da2148f39b Uwe Kleine-König 2024-02-14 319 struct pwm_chip *chip = platform_get_drvdata(dev);
bb472da2148f39b Uwe Kleine-König 2024-02-14 320 struct pwm_sifive_ddata *ddata = pwm_sifive_chip_to_ddata(chip);
9e37a53eb05114b Yash Shah 2019-06-11 321 struct pwm_device *pwm;
ceb2c2842f3664d Uwe Kleine-König 2021-07-07 322 int ch;
9e37a53eb05114b Yash Shah 2019-06-11 323
bb472da2148f39b Uwe Kleine-König 2024-02-14 324 pwmchip_remove(chip);
2375e964d541bb0 Uwe Kleine-König 2022-07-21 325 clk_notifier_unregister(ddata->clk, &ddata->notifier);
2375e964d541bb0 Uwe Kleine-König 2022-07-21 326
bb472da2148f39b Uwe Kleine-König 2024-02-14 327 for (ch = 0; ch < chip->npwm; ch++) {
bb472da2148f39b Uwe Kleine-König 2024-02-14 328 pwm = &chip->pwms[ch];
ace41d7564e655c Uwe Kleine-König 2022-07-21 329 if (pwm->state.enabled)
9e37a53eb05114b Yash Shah 2019-06-11 330 clk_disable(ddata->clk);
ace41d7564e655c Uwe Kleine-König 2022-07-21 331 }
9e37a53eb05114b Yash Shah 2019-06-11 332 }
9e37a53eb05114b Yash Shah 2019-06-11 333
9e37a53eb05114b Yash Shah 2019-06-11 334 static const struct of_device_id pwm_sifive_of_match[] = {
9e37a53eb05114b Yash Shah 2019-06-11 335 { .compatible = "sifive,pwm0" },
9e37a53eb05114b Yash Shah 2019-06-11 336 {},
9e37a53eb05114b Yash Shah 2019-06-11 337 };
9e37a53eb05114b Yash Shah 2019-06-11 338 MODULE_DEVICE_TABLE(of, pwm_sifive_of_match);
9e37a53eb05114b Yash Shah 2019-06-11 339
9e37a53eb05114b Yash Shah 2019-06-11 340 static struct platform_driver pwm_sifive_driver = {
9e37a53eb05114b Yash Shah 2019-06-11 341 .probe = pwm_sifive_probe,
8db7fdffaaf6cc9 Uwe Kleine-König 2024-09-09 342 .remove = pwm_sifive_remove,
9e37a53eb05114b Yash Shah 2019-06-11 343 .driver = {
9e37a53eb05114b Yash Shah 2019-06-11 344 .name = "pwm-sifive",
9e37a53eb05114b Yash Shah 2019-06-11 345 .of_match_table = pwm_sifive_of_match,
9e37a53eb05114b Yash Shah 2019-06-11 346 },
9e37a53eb05114b Yash Shah 2019-06-11 347 };
9e37a53eb05114b Yash Shah 2019-06-11 348 module_platform_driver(pwm_sifive_driver);
9e37a53eb05114b Yash Shah 2019-06-11 349
9e37a53eb05114b Yash Shah 2019-06-11 350 MODULE_DESCRIPTION("SiFive PWM driver");
9e37a53eb05114b Yash Shah 2019-06-11 @351 MODULE_LICENSE("GPL v2");
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH v12 4/5] pwm: sifive: Fix rounding issues in apply and get_state functions
2025-04-22 8:53 ` [PATCH v12 4/5] pwm: sifive: Fix rounding issues in apply and get_state functions Nylon Chen
2025-04-23 1:34 ` kernel test robot
@ 2025-04-23 4:08 ` kernel test robot
1 sibling, 0 replies; 8+ messages in thread
From: kernel test robot @ 2025-04-23 4:08 UTC (permalink / raw)
To: Nylon Chen, Conor Dooley, Rob Herring, Krzysztof Kozlowski,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Samuel Holland,
Uwe Kleine-König
Cc: llvm, oe-kbuild-all, linux-riscv, devicetree, linux-kernel,
linux-pwm, Nylon Chen, Zong Li
Hi Nylon,
kernel test robot noticed the following build errors:
[auto build test ERROR on robh/for-next]
[also build test ERROR on linus/master v6.15-rc3 next-20250422]
[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/Nylon-Chen/riscv-dts-sifive-unleashed-unmatched-Remove-PWM-controlled-LED-s-active-low-properties/20250422-165644
base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
patch link: https://lore.kernel.org/r/20250422085312.812877-5-nylon.chen%40sifive.com
patch subject: [PATCH v12 4/5] pwm: sifive: Fix rounding issues in apply and get_state functions
config: arm-randconfig-004-20250423 (https://download.01.org/0day-ci/archive/20250423/202504231136.Cr4O6Zg5-lkp@intel.com/config)
compiler: clang version 21.0.0git (https://github.com/llvm/llvm-project f819f46284f2a79790038e1f6649172789734ae8)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250423/202504231136.Cr4O6Zg5-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/202504231136.Cr4O6Zg5-lkp@intel.com/
All errors (new ones prefixed by >>):
>> drivers/pwm/pwm-sifive.c:161:2: error: unterminated function-like macro invocation
161 | do_div(frac, state->period;
| ^
include/asm-generic/div64.h:180:10: note: macro 'do_div' defined here
180 | # define do_div(n,base) ({ \
| ^
>> drivers/pwm/pwm-sifive.c:351:26: error: expected '}'
351 | MODULE_LICENSE("GPL v2");
| ^
drivers/pwm/pwm-sifive.c:134:1: note: to match this '{'
134 | {
| ^
2 errors generated.
vim +161 drivers/pwm/pwm-sifive.c
131
132 static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm,
133 const struct pwm_state *state)
134 {
135 struct pwm_sifive_ddata *ddata = pwm_sifive_chip_to_ddata(chip);
136 struct pwm_state cur_state;
137 unsigned int duty_cycle;
138 unsigned long long num;
139 bool enabled;
140 int ret = 0;
141 u32 frac, inactive;
142
143 if (state->polarity != PWM_POLARITY_NORMAL)
144 return -EINVAL;
145
146 cur_state = pwm->state;
147 enabled = cur_state.enabled;
148
149 duty_cycle = state->duty_cycle;
150 if (!state->enabled)
151 duty_cycle = 0;
152
153 /*
154 * The problem of output producing mixed setting as mentioned at top,
155 * occurs here. To minimize the window for this problem, we are
156 * calculating the register values first and then writing them
157 * consecutively
158 */
159 num = (u64)duty_cycle * (1U << PWM_SIFIVE_CMPWIDTH);
160 frac = num;
> 161 do_div(frac, state->period;
162 /* The hardware cannot generate a 0% duty cycle */
163 frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1);
164 inactive = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - frac;
165
166 mutex_lock(&ddata->lock);
167 if (state->period != ddata->approx_period) {
168 /*
169 * Don't let a 2nd user change the period underneath the 1st user.
170 * However if ddate->approx_period == 0 this is the first time we set
171 * any period, so let whoever gets here first set the period so other
172 * users who agree on the period won't fail.
173 */
174 if (ddata->user_count != 1 && ddata->approx_period) {
175 mutex_unlock(&ddata->lock);
176 return -EBUSY;
177 }
178 ddata->approx_period = state->period;
179 pwm_sifive_update_clock(ddata, clk_get_rate(ddata->clk));
180 }
181 mutex_unlock(&ddata->lock);
182
183 /*
184 * If the PWM is enabled the clk is already on. So only enable it
185 * conditionally to have it on exactly once afterwards independent of
186 * the PWM state.
187 */
188 if (!enabled) {
189 ret = clk_enable(ddata->clk);
190 if (ret) {
191 dev_err(pwmchip_parent(chip), "Enable clk failed\n");
192 return ret;
193 }
194 }
195
196 writel(inactive, ddata->regs + PWM_SIFIVE_PWMCMP(pwm->hwpwm));
197
198 if (!state->enabled)
199 clk_disable(ddata->clk);
200
201 return 0;
202 }
203
204 static const struct pwm_ops pwm_sifive_ops = {
205 .request = pwm_sifive_request,
206 .free = pwm_sifive_free,
207 .get_state = pwm_sifive_get_state,
208 .apply = pwm_sifive_apply,
209 };
210
211 static int pwm_sifive_clock_notifier(struct notifier_block *nb,
212 unsigned long event, void *data)
213 {
214 struct clk_notifier_data *ndata = data;
215 struct pwm_sifive_ddata *ddata =
216 container_of(nb, struct pwm_sifive_ddata, notifier);
217
218 if (event == POST_RATE_CHANGE) {
219 mutex_lock(&ddata->lock);
220 pwm_sifive_update_clock(ddata, ndata->new_rate);
221 mutex_unlock(&ddata->lock);
222 }
223
224 return NOTIFY_OK;
225 }
226
227 static int pwm_sifive_probe(struct platform_device *pdev)
228 {
229 struct device *dev = &pdev->dev;
230 struct pwm_sifive_ddata *ddata;
231 struct pwm_chip *chip;
232 int ret;
233 u32 val;
234 unsigned int enabled_pwms = 0, enabled_clks = 1;
235
236 chip = devm_pwmchip_alloc(dev, 4, sizeof(*ddata));
237 if (IS_ERR(chip))
238 return PTR_ERR(chip);
239
240 ddata = pwm_sifive_chip_to_ddata(chip);
241 ddata->parent = dev;
242 mutex_init(&ddata->lock);
243 chip->ops = &pwm_sifive_ops;
244
245 ddata->regs = devm_platform_ioremap_resource(pdev, 0);
246 if (IS_ERR(ddata->regs))
247 return PTR_ERR(ddata->regs);
248
249 ddata->clk = devm_clk_get_prepared(dev, NULL);
250 if (IS_ERR(ddata->clk))
251 return dev_err_probe(dev, PTR_ERR(ddata->clk),
252 "Unable to find controller clock\n");
253
254 ret = clk_enable(ddata->clk);
255 if (ret) {
256 dev_err(dev, "failed to enable clock for pwm: %d\n", ret);
257 return ret;
258 }
259
260 val = readl(ddata->regs + PWM_SIFIVE_PWMCFG);
261 if (val & PWM_SIFIVE_PWMCFG_EN_ALWAYS) {
262 unsigned int i;
263
264 for (i = 0; i < chip->npwm; ++i) {
265 val = readl(ddata->regs + PWM_SIFIVE_PWMCMP(i));
266 if (val > 0)
267 ++enabled_pwms;
268 }
269 }
270
271 /* The clk should be on once for each running PWM. */
272 if (enabled_pwms) {
273 while (enabled_clks < enabled_pwms) {
274 /* This is not expected to fail as the clk is already on */
275 ret = clk_enable(ddata->clk);
276 if (unlikely(ret)) {
277 dev_err_probe(dev, ret, "Failed to enable clk\n");
278 goto disable_clk;
279 }
280 ++enabled_clks;
281 }
282 } else {
283 clk_disable(ddata->clk);
284 enabled_clks = 0;
285 }
286
287 /* Watch for changes to underlying clock frequency */
288 ddata->notifier.notifier_call = pwm_sifive_clock_notifier;
289 ret = clk_notifier_register(ddata->clk, &ddata->notifier);
290 if (ret) {
291 dev_err(dev, "failed to register clock notifier: %d\n", ret);
292 goto disable_clk;
293 }
294
295 ret = pwmchip_add(chip);
296 if (ret < 0) {
297 dev_err(dev, "cannot register PWM: %d\n", ret);
298 goto unregister_clk;
299 }
300
301 platform_set_drvdata(pdev, chip);
302 dev_dbg(dev, "SiFive PWM chip registered %d PWMs\n", chip->npwm);
303
304 return 0;
305
306 unregister_clk:
307 clk_notifier_unregister(ddata->clk, &ddata->notifier);
308 disable_clk:
309 while (enabled_clks) {
310 clk_disable(ddata->clk);
311 --enabled_clks;
312 }
313
314 return ret;
315 }
316
317 static void pwm_sifive_remove(struct platform_device *dev)
318 {
319 struct pwm_chip *chip = platform_get_drvdata(dev);
320 struct pwm_sifive_ddata *ddata = pwm_sifive_chip_to_ddata(chip);
321 struct pwm_device *pwm;
322 int ch;
323
324 pwmchip_remove(chip);
325 clk_notifier_unregister(ddata->clk, &ddata->notifier);
326
327 for (ch = 0; ch < chip->npwm; ch++) {
328 pwm = &chip->pwms[ch];
329 if (pwm->state.enabled)
330 clk_disable(ddata->clk);
331 }
332 }
333
334 static const struct of_device_id pwm_sifive_of_match[] = {
335 { .compatible = "sifive,pwm0" },
336 {},
337 };
338 MODULE_DEVICE_TABLE(of, pwm_sifive_of_match);
339
340 static struct platform_driver pwm_sifive_driver = {
341 .probe = pwm_sifive_probe,
342 .remove = pwm_sifive_remove,
343 .driver = {
344 .name = "pwm-sifive",
345 .of_match_table = pwm_sifive_of_match,
346 },
347 };
348 module_platform_driver(pwm_sifive_driver);
349
350 MODULE_DESCRIPTION("SiFive PWM driver");
> 351 MODULE_LICENSE("GPL v2");
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v12 5/5] pwm: sifive: clarify inverted compare logic in comments
2025-04-22 8:53 [PATCH v12 0/5] Change PWM-controlled LED pin active mode and algorithm Nylon Chen
` (3 preceding siblings ...)
2025-04-22 8:53 ` [PATCH v12 4/5] pwm: sifive: Fix rounding issues in apply and get_state functions Nylon Chen
@ 2025-04-22 8:53 ` Nylon Chen
4 siblings, 0 replies; 8+ messages in thread
From: Nylon Chen @ 2025-04-22 8:53 UTC (permalink / raw)
To: Conor Dooley, Rob Herring, Krzysztof Kozlowski, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Samuel Holland, Uwe Kleine-König
Cc: linux-riscv, devicetree, linux-kernel, linux-pwm, Nylon Chen, Zong Li
The reference manual says "pwms >= pwmcmpX -> HIGH", but in Figure 29 pwmcmpXcenter
is forced to 0 via an XOR, so hardware actually outputs HIGH when pwms < pwmcmpX.
Thus pwmcmp holds the off-period count, and the driver must invert it
to expose a normal active-high interface.
Co-developed-by: Zong Li <zong.li@sifive.com>
Signed-off-by: Zong Li <zong.li@sifive.com>
Signed-off-by: Nylon Chen <nylon.chen@sifive.com>
---
drivers/pwm/pwm-sifive.c | 24 +++++++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c
index 065d6d2b84bc..a1f13801bca4 100644
--- a/drivers/pwm/pwm-sifive.c
+++ b/drivers/pwm/pwm-sifive.c
@@ -4,11 +4,28 @@
* For SiFive's PWM IP block documentation please refer Chapter 14 of
* Reference Manual : https://static.dev.sifive.com/FU540-C000-v1.0.pdf
*
+ * PWM output inversion: According to the SiFive Reference manual
+ * the output of each comparator is high whenever the value of pwms is
+ * greater than or equal to the corresponding pwmcmpX[Reference Manual].
+ *
+ * Figure 29 in the same manual shows that the pwmcmpXcenter bit is
+ * hard-tied to 0 (XNOR), which effectively inverts the comparison so that
+ * the output goes HIGH when `pwms < pwmcmpX`.
+ *
+ * In other words, each pwmcmp register actually defines the **inactive**
+ * (low) period of the pulse, not the active time exactly opposite to what
+ * the documentation text implies.
+ *
+ * To compensate, this driver always **inverts** the duty value when reading
+ * or writing pwmcmp registers , so that users interact with a conventional
+ * **active-high** PWM interface.
+ *
+ *
* Limitations:
* - When changing both duty cycle and period, we cannot prevent in
* software that the output might produce a period with mixed
* settings (new period length and old duty cycle).
- * - The hardware cannot generate a 100% duty cycle.
+ * - The hardware cannot generate a 0% duty cycle.
* - The hardware generates only inverted output.
*/
#include <linux/clk.h>
@@ -113,6 +130,10 @@ static int pwm_sifive_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
u32 duty, val, inactive;
inactive = readl(ddata->regs + PWM_SIFIVE_PWMCMP(pwm->hwpwm));
+ /*
+ * PWM hardware uses 'inactive' counts in pwmcmp, so invert to get actual duty.
+ * Here, 'inactive' is the low time and we compute duty as max_count - inactive.
+ */
duty = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - inactive;
state->enabled = duty > 0;
@@ -161,6 +182,7 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm,
do_div(frac, state->period;
/* The hardware cannot generate a 0% duty cycle */
frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1);
+ /* pwmcmp register must be loaded with the inactive(invert the duty) */
inactive = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - frac;
mutex_lock(&ddata->lock);
--
2.34.1
^ permalink raw reply [flat|nested] 8+ messages in thread