]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
pwm: mediatek: Lock and cache clock rate
authorUwe Kleine-König <u.kleine-koenig@baylibre.com>
Fri, 25 Jul 2025 15:45:11 +0000 (17:45 +0200)
committerUwe Kleine-König <ukleinek@kernel.org>
Mon, 15 Sep 2025 09:39:45 +0000 (11:39 +0200)
This simplifies error handling and reduces the amount of clk_get_rate()
calls.

While touching the clk handling also allocate the clock array as part of
driver data and lock the clock rate to ensure that the output doesn't
change unexpectedly.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Link: https://lore.kernel.org/r/20250725154506.2610172-17-u.kleine-koenig@baylibre.com
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
drivers/pwm/pwm-mediatek.c

index 434ddc57f5dc3e338e88c2671ed22b85e6a91383..4291072a13a7f6e50e1c46905925de17c9639dc4 100644 (file)
@@ -49,15 +49,18 @@ struct pwm_mediatek_of_data {
  * @regs: base address of PWM chip
  * @clk_top: the top clock generator
  * @clk_main: the clock used by PWM core
- * @clk_pwms: the clock used by each PWM channel
  * @soc: pointer to chip's platform data
+ * @clk_pwms: the clock and clkrate used by each PWM channel
  */
 struct pwm_mediatek_chip {
        void __iomem *regs;
        struct clk *clk_top;
        struct clk *clk_main;
-       struct clk **clk_pwms;
        const struct pwm_mediatek_of_data *soc;
+       struct {
+               struct clk *clk;
+               unsigned long rate;
+       } clk_pwms[];
 };
 
 static inline struct pwm_mediatek_chip *
@@ -79,12 +82,28 @@ static int pwm_mediatek_clk_enable(struct pwm_mediatek_chip *pc,
        if (ret < 0)
                goto disable_clk_top;
 
-       ret = clk_prepare_enable(pc->clk_pwms[hwpwm]);
+       ret = clk_prepare_enable(pc->clk_pwms[hwpwm].clk);
        if (ret < 0)
                goto disable_clk_main;
 
+       if (!pc->clk_pwms[hwpwm].rate) {
+               pc->clk_pwms[hwpwm].rate = clk_get_rate(pc->clk_pwms[hwpwm].clk);
+
+               /*
+                * With the clk running with not more than 1 GHz the
+                * calculations in .apply() won't overflow.
+                */
+               if (!pc->clk_pwms[hwpwm].rate ||
+                   pc->clk_pwms[hwpwm].rate > 1000000000) {
+                       ret = -EINVAL;
+                       goto disable_clk_hwpwm;
+               }
+       }
+
        return 0;
 
+disable_clk_hwpwm:
+       clk_disable_unprepare(pc->clk_pwms[hwpwm].clk);
 disable_clk_main:
        clk_disable_unprepare(pc->clk_main);
 disable_clk_top:
@@ -96,7 +115,7 @@ disable_clk_top:
 static void pwm_mediatek_clk_disable(struct pwm_mediatek_chip *pc,
                                     unsigned int hwpwm)
 {
-       clk_disable_unprepare(pc->clk_pwms[hwpwm]);
+       clk_disable_unprepare(pc->clk_pwms[hwpwm].clk);
        clk_disable_unprepare(pc->clk_main);
        clk_disable_unprepare(pc->clk_top);
 }
@@ -150,15 +169,7 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm,
        if (ret < 0)
                return ret;
 
-       clk_rate = clk_get_rate(pc->clk_pwms[pwm->hwpwm]);
-       /*
-        * With the clk running with not more than 1 GHz the calculations below
-        * won't overflow
-        */
-       if (!clk_rate || clk_rate > 1000000000) {
-               ret = -EINVAL;
-               goto out;
-       }
+       clk_rate = pc->clk_pwms[pwm->hwpwm].rate;
 
        /* Make sure we use the bus clock and not the 26MHz clock */
        if (pc->soc->pwm_ck_26m_sel_reg)
@@ -277,11 +288,7 @@ static int pwm_mediatek_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
                u32 clkdiv, cnt_period, cnt_duty;
                unsigned long clk_rate;
 
-               clk_rate = clk_get_rate(pc->clk_pwms[pwm->hwpwm]);
-               if (!clk_rate) {
-                       ret = -EINVAL;
-                       goto out;
-               }
+               clk_rate = pc->clk_pwms[pwm->hwpwm].rate;
 
                state->enabled = true;
                state->polarity = PWM_POLARITY_NORMAL;
@@ -306,7 +313,6 @@ static int pwm_mediatek_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
                state->enabled = false;
        }
 
-out:
        pwm_mediatek_clk_disable(pc, pwm->hwpwm);
 
        return ret;
@@ -370,7 +376,8 @@ static int pwm_mediatek_probe(struct platform_device *pdev)
 
        soc = of_device_get_match_data(&pdev->dev);
 
-       chip = devm_pwmchip_alloc(&pdev->dev, soc->num_pwms, sizeof(*pc));
+       chip = devm_pwmchip_alloc(&pdev->dev, soc->num_pwms,
+                                 sizeof(*pc) + soc->num_pwms * sizeof(*pc->clk_pwms));
        if (IS_ERR(chip))
                return PTR_ERR(chip);
        pc = to_pwm_mediatek_chip(chip);
@@ -381,11 +388,6 @@ static int pwm_mediatek_probe(struct platform_device *pdev)
        if (IS_ERR(pc->regs))
                return PTR_ERR(pc->regs);
 
-       pc->clk_pwms = devm_kmalloc_array(&pdev->dev, soc->num_pwms,
-                                   sizeof(*pc->clk_pwms), GFP_KERNEL);
-       if (!pc->clk_pwms)
-               return -ENOMEM;
-
        pc->clk_top = devm_clk_get(&pdev->dev, "top");
        if (IS_ERR(pc->clk_top))
                return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk_top),
@@ -401,10 +403,15 @@ static int pwm_mediatek_probe(struct platform_device *pdev)
 
                snprintf(name, sizeof(name), "pwm%d", i + 1);
 
-               pc->clk_pwms[i] = devm_clk_get(&pdev->dev, name);
-               if (IS_ERR(pc->clk_pwms[i]))
-                       return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk_pwms[i]),
+               pc->clk_pwms[i].clk = devm_clk_get(&pdev->dev, name);
+               if (IS_ERR(pc->clk_pwms[i].clk))
+                       return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk_pwms[i].clk),
                                             "Failed to get %s clock\n", name);
+
+               ret = devm_clk_rate_exclusive_get(&pdev->dev, pc->clk_pwms[i].clk);
+               if (ret)
+                       return dev_err_probe(&pdev->dev, ret,
+                                            "Failed to lock clock rate for %s\n", name);
        }
 
        ret = pwm_mediatek_init_used_clks(pc);