clk: ingenic-tcu: Fix missing TCU clock for X1000 SoCs
authorAidan MacDonald <aidanmacdonald.0x0@gmail.com>
Tue, 12 Apr 2022 12:27:50 +0000 (13:27 +0100)
committerStephen Boyd <sboyd@kernel.org>
Thu, 19 May 2022 00:30:36 +0000 (17:30 -0700)
The TCU clock gate on X1000 wasn't requested by the driver and could
be gated automatically later on in boot, which prevents timers from
running and breaks PWM.

Add a workaround to support old device trees that don't specify the
"tcu" clock gate. In this case the kernel will print a warning and
attempt to continue without the clock, which is wrong, but it could
work if "clk_ignore_unused" is in the kernel arguments.

Signed-off-by: Aidan MacDonald <aidanmacdonald.0x0@gmail.com>
Link: https://lore.kernel.org/r/20220412122750.279058-3-aidanmacdonald.0x0@gmail.com
Reviewed-by: Paul Cercueil <paul@crapouillou.net>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
drivers/clk/ingenic/tcu.c

index 77acfbe..201bf6e 100644 (file)
@@ -31,6 +31,7 @@ struct ingenic_soc_info {
        unsigned int num_channels;
        bool has_ost;
        bool has_tcu_clk;
+       bool allow_missing_tcu_clk;
 };
 
 struct ingenic_tcu_clk_info {
@@ -320,7 +321,8 @@ static const struct ingenic_soc_info jz4770_soc_info = {
 static const struct ingenic_soc_info x1000_soc_info = {
        .num_channels = 8,
        .has_ost = false, /* X1000 has OST, but it not belong TCU */
-       .has_tcu_clk = false,
+       .has_tcu_clk = true,
+       .allow_missing_tcu_clk = true,
 };
 
 static const struct of_device_id __maybe_unused ingenic_tcu_of_match[] __initconst = {
@@ -355,14 +357,27 @@ static int __init ingenic_tcu_probe(struct device_node *np)
                tcu->clk = of_clk_get_by_name(np, "tcu");
                if (IS_ERR(tcu->clk)) {
                        ret = PTR_ERR(tcu->clk);
-                       pr_crit("Cannot get TCU clock\n");
-                       goto err_free_tcu;
-               }
 
-               ret = clk_prepare_enable(tcu->clk);
-               if (ret) {
-                       pr_crit("Unable to enable TCU clock\n");
-                       goto err_put_clk;
+                       /*
+                        * Old device trees for some SoCs did not include the
+                        * TCU clock because this driver (incorrectly) didn't
+                        * use it. In this case we complain loudly and attempt
+                        * to continue without the clock, which might work if
+                        * booting with workarounds like "clk_ignore_unused".
+                        */
+                       if (tcu->soc_info->allow_missing_tcu_clk && ret == -EINVAL) {
+                               pr_warn("TCU clock missing from device tree, please update your device tree\n");
+                               tcu->clk = NULL;
+                       } else {
+                               pr_crit("Cannot get TCU clock from device tree\n");
+                               goto err_free_tcu;
+                       }
+               } else {
+                       ret = clk_prepare_enable(tcu->clk);
+                       if (ret) {
+                               pr_crit("Unable to enable TCU clock\n");
+                               goto err_put_clk;
+                       }
                }
        }
 
@@ -432,10 +447,10 @@ err_unregister_timer_clocks:
                        clk_hw_unregister(tcu->clocks->hws[i]);
        kfree(tcu->clocks);
 err_clk_disable:
-       if (tcu->soc_info->has_tcu_clk)
+       if (tcu->clk)
                clk_disable_unprepare(tcu->clk);
 err_put_clk:
-       if (tcu->soc_info->has_tcu_clk)
+       if (tcu->clk)
                clk_put(tcu->clk);
 err_free_tcu:
        kfree(tcu);