linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 1/4] pwm: jz4740: Drop dependency on MACH_INGENIC
@ 2020-05-27 11:52 Paul Cercueil
  2020-05-27 11:52 ` [PATCH v3 2/4] pwm: jz4740: Enhance precision in calculation of duty cycle Paul Cercueil
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Paul Cercueil @ 2020-05-27 11:52 UTC (permalink / raw)
  To: Thierry Reding, Uwe Kleine-König
  Cc: od, linux-pwm, linux-kernel, Paul Cercueil

Depending on MACH_INGENIC prevent us from creating a generic kernel that
works on more than one MIPS board. Instead, we just depend on MIPS being
set.

Acked-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
---

Notes:
    v2: New patch. I don't consider this a fix but an enhancement, since the old
    	behaviour was in place since the driver was born in ~2010, so no Fixes tag.
    v3: Commit message changes (invert Acked-by / Signed-off-by)

 drivers/pwm/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index eebbc917ac97..7814e5b2cad7 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -234,7 +234,7 @@ config PWM_IMX_TPM
 
 config PWM_JZ4740
 	tristate "Ingenic JZ47xx PWM support"
-	depends on MACH_INGENIC
+	depends on MIPS
 	depends on COMMON_CLK
 	select MFD_SYSCON
 	help
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v3 2/4] pwm: jz4740: Enhance precision in calculation of duty cycle
  2020-05-27 11:52 [PATCH v3 1/4] pwm: jz4740: Drop dependency on MACH_INGENIC Paul Cercueil
@ 2020-05-27 11:52 ` Paul Cercueil
  2020-05-27 11:52 ` [PATCH v3 3/4] pwm: jz4740: Make PWM start with the active part Paul Cercueil
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Paul Cercueil @ 2020-05-27 11:52 UTC (permalink / raw)
  To: Thierry Reding, Uwe Kleine-König
  Cc: od, linux-pwm, linux-kernel, Paul Cercueil, stable

Calculating the hardware value for the duty from the hardware value of
the period resulted in a precision loss versus calculating it from the
clock rate directly.

(Also remove a cast that doesn't really need to be here)

Fixes: f6b8a5700057 ("pwm: Add Ingenic JZ4740 support")
Cc: <stable@vger.kernel.org>
Suggested-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Reviewed-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
---

Notes:
    v2: New patch. I don't consider this a fix but an enhancement, since the old
    	behaviour was in place since the driver was born in ~2010, so no Fixes tag.
    v3: Add Fixes tag and Uwe's Reviewed-by

 drivers/pwm/pwm-jz4740.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c
index 3cd5c054ad9a..4fe9d99ac9a9 100644
--- a/drivers/pwm/pwm-jz4740.c
+++ b/drivers/pwm/pwm-jz4740.c
@@ -158,11 +158,11 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 	/* Calculate period value */
 	tmp = (unsigned long long)rate * state->period;
 	do_div(tmp, NSEC_PER_SEC);
-	period = (unsigned long)tmp;
+	period = tmp;
 
 	/* Calculate duty value */
-	tmp = (unsigned long long)period * state->duty_cycle;
-	do_div(tmp, state->period);
+	tmp = (unsigned long long)rate * state->duty_cycle;
+	do_div(tmp, NSEC_PER_SEC);
 	duty = period - tmp;
 
 	if (duty >= period)
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v3 3/4] pwm: jz4740: Make PWM start with the active part
  2020-05-27 11:52 [PATCH v3 1/4] pwm: jz4740: Drop dependency on MACH_INGENIC Paul Cercueil
  2020-05-27 11:52 ` [PATCH v3 2/4] pwm: jz4740: Enhance precision in calculation of duty cycle Paul Cercueil
@ 2020-05-27 11:52 ` Paul Cercueil
  2020-05-27 11:52 ` [PATCH v3 4/4] pwm: jz4740: Add support for the JZ4725B Paul Cercueil
  2020-06-02 12:41 ` [PATCH v3 1/4] pwm: jz4740: Drop dependency on MACH_INGENIC Thierry Reding
  3 siblings, 0 replies; 5+ messages in thread
From: Paul Cercueil @ 2020-05-27 11:52 UTC (permalink / raw)
  To: Thierry Reding, Uwe Kleine-König
  Cc: od, linux-pwm, linux-kernel, Paul Cercueil

The PWM in Ingenic SoCs starts in inactive state until the internal
timer reaches the duty value, then becomes active until the timer
reaches the period value. In theory, we should then use (period - duty)
as the real duty value, as a high duty value would otherwise result in
the PWM pin being inactive most of the time.

This is the reason why the duty value was inverted in the driver until
now, but it still had the problem that it would not start with the
active part.

To address this remaining issue, the common trick is to invert the
duty, and invert the polarity when the PWM is enabled.

Since the duty was already inverted, and we invert it again, we now
program the hardware for the requested duty, and simply invert the
polarity when the PWM is enabled.

Signed-off-by: Paul Cercueil <paul@crapouillou.net>
---

Notes:
    v2: Add documentation about why we invert the polarity, and
    	improve commit message
    v3: No change

 drivers/pwm/pwm-jz4740.c | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)

diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c
index 4fe9d99ac9a9..fe06ca8ce30f 100644
--- a/drivers/pwm/pwm-jz4740.c
+++ b/drivers/pwm/pwm-jz4740.c
@@ -6,7 +6,6 @@
  * Limitations:
  * - The .apply callback doesn't complete the currently running period before
  *   reconfiguring the hardware.
- * - Each period starts with the inactive part.
  */
 
 #include <linux/clk.h>
@@ -163,7 +162,7 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 	/* Calculate duty value */
 	tmp = (unsigned long long)rate * state->duty_cycle;
 	do_div(tmp, NSEC_PER_SEC);
-	duty = period - tmp;
+	duty = tmp;
 
 	if (duty >= period)
 		duty = period - 1;
@@ -189,18 +188,26 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 	regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
 			   TCU_TCSR_PWM_SD, TCU_TCSR_PWM_SD);
 
-	/* Set polarity */
-	switch (state->polarity) {
-	case PWM_POLARITY_NORMAL:
+	/*
+	 * Set polarity.
+	 *
+	 * The PWM starts in inactive state until the internal timer reaches the
+	 * duty value, then becomes active until the timer reaches the period
+	 * value. In theory, we should then use (period - duty) as the real duty
+	 * value, as a high duty value would otherwise result in the PWM pin
+	 * being inactive most of the time.
+	 *
+	 * Here, we don't do that, and instead invert the polarity of the PWM
+	 * when it is active. This trick makes the PWM start with its active
+	 * state instead of its inactive state.
+	 */
+	if ((state->polarity == PWM_POLARITY_NORMAL) ^ state->enabled)
 		regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
 				   TCU_TCSR_PWM_INITL_HIGH, 0);
-		break;
-	case PWM_POLARITY_INVERSED:
+	else
 		regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
 				   TCU_TCSR_PWM_INITL_HIGH,
 				   TCU_TCSR_PWM_INITL_HIGH);
-		break;
-	}
 
 	if (state->enabled)
 		jz4740_pwm_enable(chip, pwm);
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v3 4/4] pwm: jz4740: Add support for the JZ4725B
  2020-05-27 11:52 [PATCH v3 1/4] pwm: jz4740: Drop dependency on MACH_INGENIC Paul Cercueil
  2020-05-27 11:52 ` [PATCH v3 2/4] pwm: jz4740: Enhance precision in calculation of duty cycle Paul Cercueil
  2020-05-27 11:52 ` [PATCH v3 3/4] pwm: jz4740: Make PWM start with the active part Paul Cercueil
@ 2020-05-27 11:52 ` Paul Cercueil
  2020-06-02 12:41 ` [PATCH v3 1/4] pwm: jz4740: Drop dependency on MACH_INGENIC Thierry Reding
  3 siblings, 0 replies; 5+ messages in thread
From: Paul Cercueil @ 2020-05-27 11:52 UTC (permalink / raw)
  To: Thierry Reding, Uwe Kleine-König
  Cc: od, linux-pwm, linux-kernel, Paul Cercueil

The PWM hardware in the JZ4725B works the same as in the JZ4740, but has
only six channels available.

Signed-off-by: Paul Cercueil <paul@crapouillou.net>
---

Notes:
    v2: Simply return -EINVAL if we can't get match data
    v3: No change

 drivers/pwm/pwm-jz4740.c | 24 ++++++++++++++++++++----
 1 file changed, 20 insertions(+), 4 deletions(-)

diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c
index fe06ca8ce30f..5830ac2bdf6a 100644
--- a/drivers/pwm/pwm-jz4740.c
+++ b/drivers/pwm/pwm-jz4740.c
@@ -20,7 +20,9 @@
 #include <linux/pwm.h>
 #include <linux/regmap.h>
 
-#define NUM_PWM 8
+struct soc_info {
+	unsigned int num_pwms;
+};
 
 struct jz4740_pwm_chip {
 	struct pwm_chip chip;
@@ -36,7 +38,7 @@ static bool jz4740_pwm_can_use_chn(struct jz4740_pwm_chip *jz,
 				   unsigned int channel)
 {
 	/* Enable all TCU channels for PWM use by default except channels 0/1 */
-	u32 pwm_channels_mask = GENMASK(NUM_PWM - 1, 2);
+	u32 pwm_channels_mask = GENMASK(jz->chip.npwm - 1, 2);
 
 	device_property_read_u32(jz->chip.dev->parent,
 				 "ingenic,pwm-channels-mask",
@@ -226,6 +228,11 @@ static int jz4740_pwm_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct jz4740_pwm_chip *jz4740;
+	const struct soc_info *info;
+
+	info = device_get_match_data(dev);
+	if (!info)
+		return -EINVAL;
 
 	jz4740 = devm_kzalloc(dev, sizeof(*jz4740), GFP_KERNEL);
 	if (!jz4740)
@@ -239,7 +246,7 @@ static int jz4740_pwm_probe(struct platform_device *pdev)
 
 	jz4740->chip.dev = dev;
 	jz4740->chip.ops = &jz4740_pwm_ops;
-	jz4740->chip.npwm = NUM_PWM;
+	jz4740->chip.npwm = info->num_pwms;
 	jz4740->chip.base = -1;
 	jz4740->chip.of_xlate = of_pwm_xlate_with_flags;
 	jz4740->chip.of_pwm_n_cells = 3;
@@ -256,9 +263,18 @@ static int jz4740_pwm_remove(struct platform_device *pdev)
 	return pwmchip_remove(&jz4740->chip);
 }
 
+static const struct soc_info __maybe_unused jz4740_soc_info = {
+	.num_pwms = 8,
+};
+
+static const struct soc_info __maybe_unused jz4725b_soc_info = {
+	.num_pwms = 6,
+};
+
 #ifdef CONFIG_OF
 static const struct of_device_id jz4740_pwm_dt_ids[] = {
-	{ .compatible = "ingenic,jz4740-pwm", },
+	{ .compatible = "ingenic,jz4740-pwm", .data = &jz4740_soc_info },
+	{ .compatible = "ingenic,jz4725b-pwm", .data = &jz4725b_soc_info },
 	{},
 };
 MODULE_DEVICE_TABLE(of, jz4740_pwm_dt_ids);
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH v3 1/4] pwm: jz4740: Drop dependency on MACH_INGENIC
  2020-05-27 11:52 [PATCH v3 1/4] pwm: jz4740: Drop dependency on MACH_INGENIC Paul Cercueil
                   ` (2 preceding siblings ...)
  2020-05-27 11:52 ` [PATCH v3 4/4] pwm: jz4740: Add support for the JZ4725B Paul Cercueil
@ 2020-06-02 12:41 ` Thierry Reding
  3 siblings, 0 replies; 5+ messages in thread
From: Thierry Reding @ 2020-06-02 12:41 UTC (permalink / raw)
  To: Paul Cercueil; +Cc: Uwe Kleine-König, od, linux-pwm, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 737 bytes --]

On Wed, May 27, 2020 at 01:52:22PM +0200, Paul Cercueil wrote:
> Depending on MACH_INGENIC prevent us from creating a generic kernel that
> works on more than one MIPS board. Instead, we just depend on MIPS being
> set.
> 
> Acked-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> Signed-off-by: Paul Cercueil <paul@crapouillou.net>
> ---
> 
> Notes:
>     v2: New patch. I don't consider this a fix but an enhancement, since the old
>     	behaviour was in place since the driver was born in ~2010, so no Fixes tag.
>     v3: Commit message changes (invert Acked-by / Signed-off-by)
> 
>  drivers/pwm/Kconfig | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)

All four patches applied, thanks.

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2020-06-02 12:41 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-27 11:52 [PATCH v3 1/4] pwm: jz4740: Drop dependency on MACH_INGENIC Paul Cercueil
2020-05-27 11:52 ` [PATCH v3 2/4] pwm: jz4740: Enhance precision in calculation of duty cycle Paul Cercueil
2020-05-27 11:52 ` [PATCH v3 3/4] pwm: jz4740: Make PWM start with the active part Paul Cercueil
2020-05-27 11:52 ` [PATCH v3 4/4] pwm: jz4740: Add support for the JZ4725B Paul Cercueil
2020-06-02 12:41 ` [PATCH v3 1/4] pwm: jz4740: Drop dependency on MACH_INGENIC Thierry Reding

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).