diff options
author | Paul Cercueil <paul@crapouillou.net> | 2020-03-23 15:24:18 +0100 |
---|---|---|
committer | Thierry Reding <thierry.reding@gmail.com> | 2020-03-30 18:03:09 +0200 |
commit | ce1f9cece057843a03a6b9de361a03eb37dd3fac (patch) | |
tree | 78fa65b5503c5cce3c9934dfc888e95c11132b16 | |
parent | d3817a647059a3e5f8791e9b7225d194985aa75f (diff) | |
download | linux-ce1f9cece057843a03a6b9de361a03eb37dd3fac.tar.bz2 |
pwm: jz4740: Use clocks from TCU driver
The ingenic-timer "TCU" driver provides us with clocks, that can be
(un)gated, reparented or reclocked from devicetree, instead of having
these settings hardcoded in this driver.
The new code now uses a clk pointer per PWM (instead of a clk per
pwm-chip before). So the pointer is stored in per-pwm data now.
The calls to arch-specific timer code is replaced with standard
clock API calls to start and stop each channel's clock.
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>
Tested-by: Mathieu Malaterre <malat@debian.org>
Tested-by: Artur Rojek <contact@artur-rojek.eu>
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
-rw-r--r-- | drivers/pwm/Kconfig | 1 | ||||
-rw-r--r-- | drivers/pwm/pwm-jz4740.c | 54 |
2 files changed, 41 insertions, 14 deletions
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 0e64c62d4b6b..6729b0ba1252 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -235,6 +235,7 @@ config PWM_IMX_TPM config PWM_JZ4740 tristate "Ingenic JZ47xx PWM support" depends on MACH_INGENIC + depends on COMMON_CLK 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 9d78cc21cb12..2eb31f2c7717 100644 --- a/drivers/pwm/pwm-jz4740.c +++ b/drivers/pwm/pwm-jz4740.c @@ -24,7 +24,6 @@ struct jz4740_pwm_chip { struct pwm_chip chip; - struct clk *clk; }; static inline struct jz4740_pwm_chip *to_jz4740(struct pwm_chip *chip) @@ -34,6 +33,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 name[16]; + int err; + /* * Timers 0 and 1 are used for system tasks, so they are unavailable * for use as PWMs. @@ -41,16 +45,33 @@ 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(name, sizeof(name), "timer%u", pwm->hwpwm); + + clk = clk_get(chip->dev, name); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) != -EPROBE_DEFER) + dev_err(chip->dev, "Failed to get clock: %pe", clk); + + return PTR_ERR(clk); + } + + err = clk_prepare_enable(clk); + if (err < 0) { + clk_put(clk); + return err; + } + + pwm_set_chip_data(pwm, clk); return 0; } static void jz4740_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) { - jz4740_timer_set_ctrl(pwm->hwpwm, 0); + struct clk *clk = pwm_get_chip_data(pwm); - 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) @@ -91,17 +112,22 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { struct jz4740_pwm_chip *jz4740 = to_jz4740(pwm->chip); + struct clk *clk = pwm_get_chip_data(pwm), + *parent_clk = clk_get_parent(clk); + unsigned long rate, period, duty; unsigned long long tmp; - unsigned long period, duty; unsigned int prescaler = 0; uint16_t ctrl; + int err; - tmp = (unsigned long long)clk_get_rate(jz4740->clk) * state->period; + rate = clk_get_rate(parent_clk); + tmp = (unsigned long long)rate * state->period; do_div(tmp, 1000000000); period = tmp; while (period > 0xffff && prescaler < 6) { period >>= 2; + rate >>= 2; ++prescaler; } @@ -117,14 +143,18 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, jz4740_pwm_disable(chip, pwm); + err = clk_set_rate(clk, rate); + if (err) { + dev_err(chip->dev, "Unable to set rate: %d", err); + return err; + } + jz4740_timer_set_count(pwm->hwpwm, 0); jz4740_timer_set_duty(pwm->hwpwm, duty); jz4740_timer_set_period(pwm->hwpwm, period); - ctrl = JZ_TIMER_CTRL_PRESCALER(prescaler) | JZ_TIMER_CTRL_SRC_EXT | - JZ_TIMER_CTRL_PWM_ABBRUPT_SHUTDOWN; - - jz4740_timer_set_ctrl(pwm->hwpwm, ctrl); + ctrl = jz4740_timer_get_ctrl(pwm->hwpwm); + ctrl |= JZ_TIMER_CTRL_PWM_ABBRUPT_SHUTDOWN; switch (state->polarity) { case PWM_POLARITY_NORMAL: @@ -158,10 +188,6 @@ static int jz4740_pwm_probe(struct platform_device *pdev) if (!jz4740) return -ENOMEM; - jz4740->clk = devm_clk_get(&pdev->dev, "ext"); - if (IS_ERR(jz4740->clk)) - return PTR_ERR(jz4740->clk); - jz4740->chip.dev = &pdev->dev; jz4740->chip.ops = &jz4740_pwm_ops; jz4740->chip.npwm = NUM_PWM; |