linux-mips.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Paul Cercueil <paul@crapouillou.net>
To: Thierry Reding <thierry.reding@gmail.com>,
	Rob Herring <robh+dt@kernel.org>,
	Mark Rutland <mark.rutland@arm.com>,
	Daniel Lezcano <daniel.lezcano@linaro.org>,
	Thomas Gleixner <tglx@linutronix.de>,
	Ralf Baechle <ralf@linux-mips.org>,
	Paul Burton <paul.burton@mips.com>,
	James Hogan <jhogan@kernel.org>, Jonathan Corbet <corbet@lwn.net>
Cc: Mathieu Malaterre <malat@debian.org>,
	Ezequiel Garcia <ezequiel@collabora.co.uk>,
	PrasannaKumar Muralidharan <prasannatsmkumar@gmail.com>,
	linux-pwm@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-watchdog@vger.kernel.org,
	linux-mips@vger.kernel.org, linux-doc@vger.kernel.org,
	linux-clk@vger.kernel.org, od@zcrc.me,
	Paul Cercueil <paul@crapouillou.net>
Subject: [PATCH v8 11/26] pwm: jz4740: Use regmap and clocks from TCU driver
Date: Wed, 12 Dec 2018 23:09:06 +0100	[thread overview]
Message-ID: <20181212220922.18759-12-paul@crapouillou.net> (raw)
In-Reply-To: <20181212220922.18759-1-paul@crapouillou.net>

The ingenic-timer "TCU" driver provides us with a regmap, that we can
use to safely access the TCU registers.

It also provides us with clocks, that can be (un)gated, reparented or
reclocked from devicetree, instead of having these settings hardcoded in
this driver.

While this driver is devicetree-compatible, it is never (as of now)
probed from devicetree, so this change does not introduce a ABI problem
with current devicetree files.

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

Notes:
     v5: New patch
    
     v6: Drop requirement of probing from devicetree
    
     v7: No change

     v8: Drop useless <linux/clk-provider.h> include

 drivers/pwm/Kconfig      |   2 +
 drivers/pwm/pwm-jz4740.c | 123 ++++++++++++++++++++++++++++++-----------------
 2 files changed, 82 insertions(+), 43 deletions(-)

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 27e5dd47a01f..0343f0c1238e 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -202,6 +202,8 @@ config PWM_IMX
 config PWM_JZ4740
 	tristate "Ingenic JZ47xx PWM support"
 	depends on MACH_INGENIC
+	depends on COMMON_CLK
+	select INGENIC_TIMER
 	help
 	  Generic PWM framework driver for Ingenic JZ47xx based
 	  machines.
diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c
index a7b134af5e04..6b865c14f789 100644
--- a/drivers/pwm/pwm-jz4740.c
+++ b/drivers/pwm/pwm-jz4740.c
@@ -15,20 +15,19 @@
 
 #include <linux/clk.h>
 #include <linux/err.h>
-#include <linux/gpio.h>
 #include <linux/kernel.h>
+#include <linux/mfd/ingenic-tcu.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/pwm.h>
-
-#include <asm/mach-jz4740/timer.h>
+#include <linux/regmap.h>
 
 #define NUM_PWM 8
 
 struct jz4740_pwm_chip {
 	struct pwm_chip chip;
-	struct clk *clk;
+	struct clk *clks[NUM_PWM];
+	struct regmap *map;
 };
 
 static inline struct jz4740_pwm_chip *to_jz4740(struct pwm_chip *chip)
@@ -38,6 +37,11 @@ static inline struct jz4740_pwm_chip *to_jz4740(struct pwm_chip *chip)
 
 static int jz4740_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
 {
+	struct jz4740_pwm_chip *jz = to_jz4740(chip);
+	struct clk *clk;
+	char clk_name[16];
+	int ret;
+
 	/*
 	 * Timers 0 and 1 are used for system tasks, so they are unavailable
 	 * for use as PWMs.
@@ -45,65 +49,89 @@ static int jz4740_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
 	if (pwm->hwpwm < 2)
 		return -EBUSY;
 
-	jz4740_timer_start(pwm->hwpwm);
+	snprintf(clk_name, sizeof(clk_name), "timer%u", pwm->hwpwm);
+
+	clk = clk_get(chip->dev, clk_name);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
 
+	ret = clk_prepare_enable(clk);
+	if (ret) {
+		clk_put(clk);
+		return ret;
+	}
+
+	jz->clks[pwm->hwpwm] = clk;
 	return 0;
 }
 
 static void jz4740_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-	jz4740_timer_set_ctrl(pwm->hwpwm, 0);
+	struct jz4740_pwm_chip *jz = to_jz4740(chip);
+	struct clk *clk = jz->clks[pwm->hwpwm];
 
-	jz4740_timer_stop(pwm->hwpwm);
+	clk_disable_unprepare(clk);
+	clk_put(clk);
 }
 
 static int jz4740_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-	uint32_t ctrl = jz4740_timer_get_ctrl(pwm->pwm);
+	struct jz4740_pwm_chip *jz = to_jz4740(chip);
 
-	ctrl |= JZ_TIMER_CTRL_PWM_ENABLE;
-	jz4740_timer_set_ctrl(pwm->hwpwm, ctrl);
-	jz4740_timer_enable(pwm->hwpwm);
+	/* Enable PWM output */
+	regmap_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm),
+				TCU_TCSR_PWM_EN, TCU_TCSR_PWM_EN);
 
+	/* Start counter */
+	regmap_write(jz->map, TCU_REG_TESR, BIT(pwm->hwpwm));
 	return 0;
 }
 
 static void jz4740_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-	uint32_t ctrl = jz4740_timer_get_ctrl(pwm->hwpwm);
+	struct jz4740_pwm_chip *jz = to_jz4740(chip);
 
 	/* Disable PWM output.
 	 * In TCU2 mode (channel 1/2 on JZ4750+), this must be done before the
 	 * counter is stopped, while in TCU1 mode the order does not matter.
 	 */
-	ctrl &= ~JZ_TIMER_CTRL_PWM_ENABLE;
-	jz4740_timer_set_ctrl(pwm->hwpwm, ctrl);
+	regmap_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm),
+				TCU_TCSR_PWM_EN, 0);
 
 	/* Stop counter */
-	jz4740_timer_disable(pwm->hwpwm);
+	regmap_write(jz->map, TCU_REG_TECR, BIT(pwm->hwpwm));
 }
 
 static int jz4740_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 			     int duty_ns, int period_ns)
 {
 	struct jz4740_pwm_chip *jz4740 = to_jz4740(pwm->chip);
+	struct clk *clk = jz4740->clks[pwm->hwpwm];
+	unsigned long rate, new_rate, period, duty;
 	unsigned long long tmp;
-	unsigned long period, duty;
-	unsigned int prescaler = 0;
-	uint16_t ctrl;
+	unsigned int tcsr;
 	bool is_enabled;
 
-	tmp = (unsigned long long)clk_get_rate(jz4740->clk) * period_ns;
-	do_div(tmp, 1000000000);
-	period = tmp;
+	rate = clk_get_rate(clk);
+
+	for (;;) {
+		tmp = (unsigned long long) rate * period_ns;
+		do_div(tmp, 1000000000);
 
-	while (period > 0xffff && prescaler < 6) {
-		period >>= 2;
-		++prescaler;
+		if (tmp <= 0xffff)
+			break;
+
+		new_rate = clk_round_rate(clk, rate / 2);
+
+		if (new_rate < rate)
+			rate = new_rate;
+		else
+			return -EINVAL;
 	}
 
-	if (prescaler == 6)
-		return -EINVAL;
+	clk_set_rate(clk, rate);
+
+	period = tmp;
 
 	tmp = (unsigned long long)period * duty_ns;
 	do_div(tmp, period_ns);
@@ -112,18 +140,23 @@ static int jz4740_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 	if (duty >= period)
 		duty = period - 1;
 
-	is_enabled = jz4740_timer_is_enabled(pwm->hwpwm);
+	regmap_read(jz4740->map, TCU_REG_TER, &tcsr);
+	is_enabled = tcsr & BIT(pwm->hwpwm);
 	if (is_enabled)
 		jz4740_pwm_disable(chip, pwm);
 
-	jz4740_timer_set_count(pwm->hwpwm, 0);
-	jz4740_timer_set_duty(pwm->hwpwm, duty);
-	jz4740_timer_set_period(pwm->hwpwm, period);
+	/* Set abrupt shutdown */
+	regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
+				TCU_TCSR_PWM_SD, TCU_TCSR_PWM_SD);
+
+	/* Reset counter to 0 */
+	regmap_write(jz4740->map, TCU_REG_TCNTc(pwm->hwpwm), 0);
 
-	ctrl = JZ_TIMER_CTRL_PRESCALER(prescaler) | JZ_TIMER_CTRL_SRC_EXT |
-		JZ_TIMER_CTRL_PWM_ABBRUPT_SHUTDOWN;
+	/* Set duty */
+	regmap_write(jz4740->map, TCU_REG_TDHRc(pwm->hwpwm), duty);
 
-	jz4740_timer_set_ctrl(pwm->hwpwm, ctrl);
+	/* Set period */
+	regmap_write(jz4740->map, TCU_REG_TDFRc(pwm->hwpwm), period);
 
 	if (is_enabled)
 		jz4740_pwm_enable(chip, pwm);
@@ -134,18 +167,19 @@ static int jz4740_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 static int jz4740_pwm_set_polarity(struct pwm_chip *chip,
 		struct pwm_device *pwm, enum pwm_polarity polarity)
 {
-	uint32_t ctrl = jz4740_timer_get_ctrl(pwm->pwm);
+	struct jz4740_pwm_chip *jz = to_jz4740(chip);
 
 	switch (polarity) {
 	case PWM_POLARITY_NORMAL:
-		ctrl &= ~JZ_TIMER_CTRL_PWM_ACTIVE_LOW;
+		regmap_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm),
+				TCU_TCSR_PWM_INITL_HIGH, 0);
 		break;
 	case PWM_POLARITY_INVERSED:
-		ctrl |= JZ_TIMER_CTRL_PWM_ACTIVE_LOW;
+		regmap_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm),
+			TCU_TCSR_PWM_INITL_HIGH, TCU_TCSR_PWM_INITL_HIGH);
 		break;
 	}
 
-	jz4740_timer_set_ctrl(pwm->hwpwm, ctrl);
 	return 0;
 }
 
@@ -162,16 +196,19 @@ static const struct pwm_ops jz4740_pwm_ops = {
 static int jz4740_pwm_probe(struct platform_device *pdev)
 {
 	struct jz4740_pwm_chip *jz4740;
+	struct device *dev = &pdev->dev;
 
-	jz4740 = devm_kzalloc(&pdev->dev, sizeof(*jz4740), GFP_KERNEL);
+	jz4740 = devm_kzalloc(dev, sizeof(*jz4740), GFP_KERNEL);
 	if (!jz4740)
 		return -ENOMEM;
 
-	jz4740->clk = devm_clk_get(&pdev->dev, "ext");
-	if (IS_ERR(jz4740->clk))
-		return PTR_ERR(jz4740->clk);
+	jz4740->map = dev_get_regmap(dev->parent, NULL);
+	if (!jz4740->map) {
+		dev_err(dev, "regmap not found\n");
+		return -EINVAL;
+	}
 
-	jz4740->chip.dev = &pdev->dev;
+	jz4740->chip.dev = dev;
 	jz4740->chip.ops = &jz4740_pwm_ops;
 	jz4740->chip.npwm = NUM_PWM;
 	jz4740->chip.base = -1;
-- 
2.11.0


  parent reply	other threads:[~2018-12-12 22:19 UTC|newest]

Thread overview: 66+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-12-12 22:08 [PATCH v8 00/26] Ingenic TCU patchset v8 Paul Cercueil
2018-12-12 22:08 ` [PATCH v8 01/26] dt-bindings: ingenic: Add DT bindings for TCU clocks Paul Cercueil
2018-12-17 23:18   ` Stephen Boyd
2018-12-12 22:08 ` [PATCH v8 02/26] doc: Add doc for the Ingenic TCU hardware Paul Cercueil
2018-12-12 22:08 ` [PATCH v8 03/26] dt-bindings: Add doc for the Ingenic TCU drivers Paul Cercueil
2018-12-17 21:05   ` Rob Herring
2018-12-17 22:03     ` Paul Cercueil
2018-12-18 16:36       ` Rob Herring
2018-12-22 11:09         ` Paul Cercueil
2018-12-12 22:08 ` [PATCH v8 04/26] clocksource: Add a new timer-ingenic driver Paul Cercueil
2018-12-12 22:09 ` [PATCH v8 05/26] clocksource: Add driver for the Ingenic JZ47xx OST Paul Cercueil
2019-01-23 12:58   ` Mathieu Malaterre
2019-01-23 14:31     ` Guenter Roeck
2019-01-23 17:25       ` Paul Cercueil
2019-01-23 18:01         ` Guenter Roeck
2019-01-24 19:28           ` Stephen Boyd
2019-01-24 20:46             ` Paul Cercueil
2019-01-24 22:46               ` Stephen Boyd
2019-01-24 22:53                 ` Paul Cercueil
2019-02-23  3:17                   ` Paul Cercueil
2019-02-25 18:05                     ` Stephen Boyd
2019-02-27 23:54                       ` Paul Cercueil
2019-01-23 17:27     ` Paul Cercueil
2018-12-12 22:09 ` [PATCH v8 06/26] MAINTAINERS: Add myself as maintainer for Ingenic TCU drivers Paul Cercueil
2018-12-12 22:09 ` [PATCH v8 07/26] watchdog: jz4740: Use WDT clock provided by TCU driver Paul Cercueil
2018-12-12 22:09 ` [PATCH v8 08/26] watchdog: jz4740: Use regmap " Paul Cercueil
2018-12-12 22:09 ` [PATCH v8 09/26] watchdog: jz4740: Avoid starting watchdog in set_timeout Paul Cercueil
2018-12-12 22:09 ` [PATCH v8 10/26] watchdog: jz4740: Drop dependency on MACH_JZ47xx, use COMPILE_TEST Paul Cercueil
2018-12-12 22:09 ` Paul Cercueil [this message]
2018-12-13  9:30   ` [PATCH v8 11/26] pwm: jz4740: Use regmap and clocks from TCU driver Uwe Kleine-König
2018-12-13 14:34     ` Paul Cercueil
2018-12-12 22:09 ` [PATCH v8 12/26] pwm: jz4740: Allow selection of PWM channels 0 and 1 Paul Cercueil
2018-12-13  9:18   ` Uwe Kleine-König
2018-12-13 13:58     ` Paul Cercueil
2018-12-13 20:32       ` Uwe Kleine-König
2018-12-16 13:36         ` Paul Cercueil
2018-12-17  7:43           ` Uwe Kleine-König
2018-12-12 22:09 ` [PATCH v8 13/26] pwm: jz4740: Drop dependency on MACH_INGENIC, use COMPILE_TEST Paul Cercueil
2018-12-12 22:09 ` [PATCH v8 14/26] pwm: jz4740: Remove unused devicetree compatible strings Paul Cercueil
2018-12-12 22:09 ` [PATCH v8 15/26] pwm: jz4740: Add support for the JZ4725B Paul Cercueil
2018-12-13  9:24   ` Uwe Kleine-König
2018-12-13 14:03     ` Paul Cercueil
2018-12-13 16:18       ` Thierry Reding
2018-12-13 20:42       ` Uwe Kleine-König
2018-12-14 13:50         ` Linus Walleij
2018-12-14 14:26           ` Uwe Kleine-König
2018-12-14 14:56             ` Linus Walleij
2018-12-16 14:18             ` Paul Cercueil
2018-12-17  7:53               ` Uwe Kleine-König
2018-12-20 17:39                 ` Thierry Reding
2018-12-20 20:58                   ` Uwe Kleine-König
2018-12-12 22:09 ` [PATCH v8 16/26] clk: jz4740: Add TCU clock Paul Cercueil
2018-12-12 22:09 ` [PATCH v8 17/26] MIPS: Kconfig: Select TCU timer driver when MACH_INGENIC is set Paul Cercueil
2018-12-12 22:09 ` [PATCH v8 18/26] MIPS: jz4740: Add DTS nodes for the TCU drivers Paul Cercueil
2018-12-12 22:09 ` [PATCH v8 19/26] MIPS: qi_lb60: Move PWM devices to devicetree Paul Cercueil
2018-12-12 22:09 ` [PATCH v8 20/26] MIPS: qi_lb60: Reduce system timer and clocksource to 750 kHz Paul Cercueil
2018-12-12 22:12 ` [PATCH v8 21/26] MIPS: CI20: Reduce system timer and clocksource to 3 MHz Paul Cercueil
2018-12-12 22:12 ` [PATCH v8 22/26] MIPS: CI20: defconfig: enable OST driver Paul Cercueil
2018-12-12 22:13 ` [PATCH v8 23/26] MIPS: GCW0: Move clocksource to TCU channel 2 Paul Cercueil
2018-12-12 22:13 ` [PATCH v8 24/26] MIPS: GCW0: Reduce system timer and clocksource to 750 kHz Paul Cercueil
2018-12-12 22:14 ` [PATCH v8 25/26] MIPS: GCW0: defconfig: Enable OST, watchdog, PWM drivers Paul Cercueil
2018-12-12 22:15 ` [PATCH v8 26/26] MIPS: jz4740: Drop obsolete code Paul Cercueil
2019-01-24 21:26 ` [PATCH v8 00/26] Ingenic TCU patchset v8 Mathieu Malaterre
2019-01-24 21:41   ` Paul Cercueil
2019-01-25  8:21     ` Mathieu Malaterre
2019-01-25 17:04       ` Paul Cercueil

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20181212220922.18759-12-paul@crapouillou.net \
    --to=paul@crapouillou.net \
    --cc=corbet@lwn.net \
    --cc=daniel.lezcano@linaro.org \
    --cc=devicetree@vger.kernel.org \
    --cc=ezequiel@collabora.co.uk \
    --cc=jhogan@kernel.org \
    --cc=linux-clk@vger.kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mips@vger.kernel.org \
    --cc=linux-pwm@vger.kernel.org \
    --cc=linux-watchdog@vger.kernel.org \
    --cc=malat@debian.org \
    --cc=mark.rutland@arm.com \
    --cc=od@zcrc.me \
    --cc=paul.burton@mips.com \
    --cc=prasannatsmkumar@gmail.com \
    --cc=ralf@linux-mips.org \
    --cc=robh+dt@kernel.org \
    --cc=tglx@linutronix.de \
    --cc=thierry.reding@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).