From ce1f9cece057843a03a6b9de361a03eb37dd3fac Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Mon, 23 Mar 2020 15:24:18 +0100 Subject: [PATCH] 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 Tested-by: Mathieu Malaterre Tested-by: Artur Rojek Signed-off-by: Thierry Reding --- drivers/pwm/Kconfig | 1 + 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;