clk: sunxi-ng: Unregister clocks/resets when unbinding
authorSamuel Holland <samuel@sholland.org>
Wed, 1 Sep 2021 05:05:19 +0000 (00:05 -0500)
committerMaxime Ripard <maxime@cerno.tech>
Mon, 13 Sep 2021 07:03:20 +0000 (09:03 +0200)
Currently, unbinding a CCU driver unmaps the device's MMIO region, while
leaving its clocks/resets and their providers registered. This can cause
a page fault later when some clock operation tries to perform MMIO. Fix
this by separating the CCU initialization from the memory allocation,
and then using a devres callback to unregister the clocks and resets.

This also fixes a memory leak of the `struct ccu_reset`, and uses the
correct owner (the specific platform driver) for the clocks and resets.

Early OF clock providers are never unregistered, and limited error
handling is possible, so they are mostly unchanged. The error reporting
is made more consistent by moving the message inside of_sunxi_ccu_probe.

Signed-off-by: Samuel Holland <samuel@sholland.org>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20210901050526.45673-2-samuel@sholland.org
23 files changed:
drivers/clk/sunxi-ng/ccu-sun4i-a10.c
drivers/clk/sunxi-ng/ccu-sun50i-a100-r.c
drivers/clk/sunxi-ng/ccu-sun50i-a100.c
drivers/clk/sunxi-ng/ccu-sun50i-a64.c
drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c
drivers/clk/sunxi-ng/ccu-sun50i-h6.c
drivers/clk/sunxi-ng/ccu-sun50i-h616.c
drivers/clk/sunxi-ng/ccu-sun5i.c
drivers/clk/sunxi-ng/ccu-sun6i-a31.c
drivers/clk/sunxi-ng/ccu-sun8i-a23.c
drivers/clk/sunxi-ng/ccu-sun8i-a33.c
drivers/clk/sunxi-ng/ccu-sun8i-a83t.c
drivers/clk/sunxi-ng/ccu-sun8i-de2.c
drivers/clk/sunxi-ng/ccu-sun8i-h3.c
drivers/clk/sunxi-ng/ccu-sun8i-r.c
drivers/clk/sunxi-ng/ccu-sun8i-r40.c
drivers/clk/sunxi-ng/ccu-sun8i-v3s.c
drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c
drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.c
drivers/clk/sunxi-ng/ccu-sun9i-a80.c
drivers/clk/sunxi-ng/ccu-suniv-f1c100s.c
drivers/clk/sunxi-ng/ccu_common.c
drivers/clk/sunxi-ng/ccu_common.h

index f32366d..bd9a878 100644 (file)
@@ -1464,7 +1464,7 @@ static void __init sun4i_ccu_init(struct device_node *node,
        val &= ~GENMASK(7, 6);
        writel(val | (2 << 6), reg + SUN4I_AHB_REG);
 
-       sunxi_ccu_probe(node, reg, desc);
+       of_sunxi_ccu_probe(node, reg, desc);
 }
 
 static void __init sun4i_a10_ccu_setup(struct device_node *node)
index a56142b..6f2a589 100644 (file)
@@ -196,7 +196,7 @@ static int sun50i_a100_r_ccu_probe(struct platform_device *pdev)
        if (IS_ERR(reg))
                return PTR_ERR(reg);
 
-       return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun50i_a100_r_ccu_desc);
+       return devm_sunxi_ccu_probe(&pdev->dev, reg, &sun50i_a100_r_ccu_desc);
 }
 
 static const struct of_device_id sun50i_a100_r_ccu_ids[] = {
index 81b48c7..913bb08 100644 (file)
@@ -1247,7 +1247,7 @@ static int sun50i_a100_ccu_probe(struct platform_device *pdev)
                writel(val, reg + sun50i_a100_usb2_clk_regs[i]);
        }
 
-       ret = sunxi_ccu_probe(pdev->dev.of_node, reg, &sun50i_a100_ccu_desc);
+       ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun50i_a100_ccu_desc);
        if (ret)
                return ret;
 
index 149cfde..54f25c6 100644 (file)
@@ -955,7 +955,7 @@ static int sun50i_a64_ccu_probe(struct platform_device *pdev)
 
        writel(0x515, reg + SUN50I_A64_PLL_MIPI_REG);
 
-       ret = sunxi_ccu_probe(pdev->dev.of_node, reg, &sun50i_a64_ccu_desc);
+       ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun50i_a64_ccu_desc);
        if (ret)
                return ret;
 
index f8909a7..f30d7eb 100644 (file)
@@ -232,7 +232,7 @@ static void __init sunxi_r_ccu_init(struct device_node *node,
                return;
        }
 
-       sunxi_ccu_probe(node, reg, desc);
+       of_sunxi_ccu_probe(node, reg, desc);
 }
 
 static void __init sun50i_h6_r_ccu_setup(struct device_node *node)
index bff446b..c0800da 100644 (file)
@@ -1240,7 +1240,7 @@ static int sun50i_h6_ccu_probe(struct platform_device *pdev)
        val |= BIT(24);
        writel(val, reg + SUN50I_H6_HDMI_CEC_CLK_REG);
 
-       return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun50i_h6_ccu_desc);
+       return devm_sunxi_ccu_probe(&pdev->dev, reg, &sun50i_h6_ccu_desc);
 }
 
 static const struct of_device_id sun50i_h6_ccu_ids[] = {
index 2253073..22eb180 100644 (file)
@@ -1141,9 +1141,7 @@ static void __init sun50i_h616_ccu_setup(struct device_node *node)
        val |= BIT(24);
        writel(val, reg + SUN50I_H616_HDMI_CEC_CLK_REG);
 
-       i = sunxi_ccu_probe(node, reg, &sun50i_h616_ccu_desc);
-       if (i)
-               pr_err("%pOF: probing clocks fails: %d\n", node, i);
+       of_sunxi_ccu_probe(node, reg, &sun50i_h616_ccu_desc);
 }
 
 CLK_OF_DECLARE(sun50i_h616_ccu, "allwinner,sun50i-h616-ccu",
index b78e9b5..1f4bc0e 100644 (file)
@@ -1012,7 +1012,7 @@ static void __init sun5i_ccu_init(struct device_node *node,
        val &= ~GENMASK(7, 6);
        writel(val | (2 << 6), reg + SUN5I_AHB_REG);
 
-       sunxi_ccu_probe(node, reg, desc);
+       of_sunxi_ccu_probe(node, reg, desc);
 }
 
 static void __init sun5i_a10s_ccu_setup(struct device_node *node)
index 9b40d53..3df5c0b 100644 (file)
@@ -1257,7 +1257,7 @@ static void __init sun6i_a31_ccu_setup(struct device_node *node)
        val |= 0x3 << 12;
        writel(val, reg + SUN6I_A31_AHB1_REG);
 
-       sunxi_ccu_probe(node, reg, &sun6i_a31_ccu_desc);
+       of_sunxi_ccu_probe(node, reg, &sun6i_a31_ccu_desc);
 
        ccu_mux_notifier_register(pll_cpu_clk.common.hw.clk,
                                  &sun6i_a31_cpu_nb);
index 103aa50..577bb23 100644 (file)
@@ -745,7 +745,7 @@ static void __init sun8i_a23_ccu_setup(struct device_node *node)
        val &= ~BIT(16);
        writel(val, reg + SUN8I_A23_PLL_MIPI_REG);
 
-       sunxi_ccu_probe(node, reg, &sun8i_a23_ccu_desc);
+       of_sunxi_ccu_probe(node, reg, &sun8i_a23_ccu_desc);
 }
 CLK_OF_DECLARE(sun8i_a23_ccu, "allwinner,sun8i-a23-ccu",
               sun8i_a23_ccu_setup);
index 91838cd..8f65cd0 100644 (file)
@@ -805,7 +805,7 @@ static void __init sun8i_a33_ccu_setup(struct device_node *node)
        val &= ~BIT(16);
        writel(val, reg + SUN8I_A33_PLL_MIPI_REG);
 
-       sunxi_ccu_probe(node, reg, &sun8i_a33_ccu_desc);
+       of_sunxi_ccu_probe(node, reg, &sun8i_a33_ccu_desc);
 
        /* Gate then ungate PLL CPU after any rate changes */
        ccu_pll_notifier_register(&sun8i_a33_pll_cpu_nb);
index 2b43452..c2ddcd2 100644 (file)
@@ -906,7 +906,7 @@ static int sun8i_a83t_ccu_probe(struct platform_device *pdev)
        sun8i_a83t_cpu_pll_fixup(reg + SUN8I_A83T_PLL_C0CPUX_REG);
        sun8i_a83t_cpu_pll_fixup(reg + SUN8I_A83T_PLL_C1CPUX_REG);
 
-       return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun8i_a83t_ccu_desc);
+       return devm_sunxi_ccu_probe(&pdev->dev, reg, &sun8i_a83t_ccu_desc);
 }
 
 static const struct of_device_id sun8i_a83t_ccu_ids[] = {
index 524f332..4b94b60 100644 (file)
@@ -342,7 +342,7 @@ static int sunxi_de2_clk_probe(struct platform_device *pdev)
                goto err_disable_mod_clk;
        }
 
-       ret = sunxi_ccu_probe(pdev->dev.of_node, reg, ccu_desc);
+       ret = devm_sunxi_ccu_probe(&pdev->dev, reg, ccu_desc);
        if (ret)
                goto err_assert_reset;
 
index 7e629a4..d2fc290 100644 (file)
@@ -1154,7 +1154,7 @@ static void __init sunxi_h3_h5_ccu_init(struct device_node *node,
        val &= ~GENMASK(19, 16);
        writel(val | (0 << 16), reg + SUN8I_H3_PLL_AUDIO_REG);
 
-       sunxi_ccu_probe(node, reg, desc);
+       of_sunxi_ccu_probe(node, reg, desc);
 
        /* Gate then ungate PLL CPU after any rate changes */
        ccu_pll_notifier_register(&sun8i_h3_pll_cpu_nb);
index 4c8c491..9e754d1 100644 (file)
@@ -265,7 +265,7 @@ static void __init sunxi_r_ccu_init(struct device_node *node,
                return;
        }
 
-       sunxi_ccu_probe(node, reg, desc);
+       of_sunxi_ccu_probe(node, reg, desc);
 }
 
 static void __init sun8i_a83t_r_ccu_setup(struct device_node *node)
index 8415341..002e0c3 100644 (file)
@@ -1346,7 +1346,7 @@ static int sun8i_r40_ccu_probe(struct platform_device *pdev)
        if (IS_ERR(regmap))
                return PTR_ERR(regmap);
 
-       ret = sunxi_ccu_probe(pdev->dev.of_node, reg, &sun8i_r40_ccu_desc);
+       ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun8i_r40_ccu_desc);
        if (ret)
                return ret;
 
index f49724a..ce150f8 100644 (file)
@@ -822,7 +822,7 @@ static void __init sun8i_v3_v3s_ccu_init(struct device_node *node,
        val &= ~GENMASK(19, 16);
        writel(val, reg + SUN8I_V3S_PLL_AUDIO_REG);
 
-       sunxi_ccu_probe(node, reg, ccu_desc);
+       of_sunxi_ccu_probe(node, reg, ccu_desc);
 }
 
 static void __init sun8i_v3s_ccu_setup(struct device_node *node)
index 6616e81..261e644 100644 (file)
@@ -246,8 +246,7 @@ static int sun9i_a80_de_clk_probe(struct platform_device *pdev)
                goto err_disable_clk;
        }
 
-       ret = sunxi_ccu_probe(pdev->dev.of_node, reg,
-                             &sun9i_a80_de_clk_desc);
+       ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun9i_a80_de_clk_desc);
        if (ret)
                goto err_assert_reset;
 
index 4b4a507..596243b 100644 (file)
@@ -117,8 +117,7 @@ static int sun9i_a80_usb_clk_probe(struct platform_device *pdev)
                return ret;
        }
 
-       ret = sunxi_ccu_probe(pdev->dev.of_node, reg,
-                             &sun9i_a80_usb_clk_desc);
+       ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun9i_a80_usb_clk_desc);
        if (ret)
                goto err_disable_clk;
 
index ef29582..97aaed0 100644 (file)
@@ -1231,7 +1231,7 @@ static int sun9i_a80_ccu_probe(struct platform_device *pdev)
        sun9i_a80_cpu_pll_fixup(reg + SUN9I_A80_PLL_C0CPUX_REG);
        sun9i_a80_cpu_pll_fixup(reg + SUN9I_A80_PLL_C1CPUX_REG);
 
-       return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun9i_a80_ccu_desc);
+       return devm_sunxi_ccu_probe(&pdev->dev, reg, &sun9i_a80_ccu_desc);
 }
 
 static const struct of_device_id sun9i_a80_ccu_ids[] = {
index 7ecc3a5..61ad7ee 100644 (file)
@@ -538,7 +538,7 @@ static void __init suniv_f1c100s_ccu_setup(struct device_node *node)
        val &= ~GENMASK(19, 16);
        writel(val | (3 << 16), reg + SUNIV_PLL_AUDIO_REG);
 
-       sunxi_ccu_probe(node, reg, &suniv_ccu_desc);
+       of_sunxi_ccu_probe(node, reg, &suniv_ccu_desc);
 
        /* Gate then ungate PLL CPU after any rate changes */
        ccu_pll_notifier_register(&suniv_pll_cpu_nb);
index 2e20e65..88cb569 100644 (file)
@@ -7,6 +7,7 @@
 
 #include <linux/clk.h>
 #include <linux/clk-provider.h>
+#include <linux/device.h>
 #include <linux/iopoll.h>
 #include <linux/slab.h>
 
 #include "ccu_gate.h"
 #include "ccu_reset.h"
 
+struct sunxi_ccu {
+       const struct sunxi_ccu_desc     *desc;
+       struct ccu_reset                reset;
+};
+
 static DEFINE_SPINLOCK(ccu_lock);
 
 void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock)
@@ -79,12 +85,15 @@ int ccu_pll_notifier_register(struct ccu_pll_nb *pll_nb)
                                     &pll_nb->clk_nb);
 }
 
-int sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
-                   const struct sunxi_ccu_desc *desc)
+static int sunxi_ccu_probe(struct sunxi_ccu *ccu, struct device *dev,
+                          struct device_node *node, void __iomem *reg,
+                          const struct sunxi_ccu_desc *desc)
 {
        struct ccu_reset *reset;
        int i, ret;
 
+       ccu->desc = desc;
+
        for (i = 0; i < desc->num_ccu_clks; i++) {
                struct ccu_common *cclk = desc->ccu_clks[i];
 
@@ -103,7 +112,10 @@ int sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
                        continue;
 
                name = hw->init->name;
-               ret = of_clk_hw_register(node, hw);
+               if (dev)
+                       ret = clk_hw_register(dev, hw);
+               else
+                       ret = of_clk_hw_register(node, hw);
                if (ret) {
                        pr_err("Couldn't register clock %d - %s\n", i, name);
                        goto err_clk_unreg;
@@ -115,15 +127,10 @@ int sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
        if (ret)
                goto err_clk_unreg;
 
-       reset = kzalloc(sizeof(*reset), GFP_KERNEL);
-       if (!reset) {
-               ret = -ENOMEM;
-               goto err_alloc_reset;
-       }
-
+       reset = &ccu->reset;
        reset->rcdev.of_node = node;
        reset->rcdev.ops = &ccu_reset_ops;
-       reset->rcdev.owner = THIS_MODULE;
+       reset->rcdev.owner = dev ? dev->driver->owner : THIS_MODULE;
        reset->rcdev.nr_resets = desc->num_resets;
        reset->base = reg;
        reset->lock = &ccu_lock;
@@ -131,13 +138,11 @@ int sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
 
        ret = reset_controller_register(&reset->rcdev);
        if (ret)
-               goto err_of_clk_unreg;
+               goto err_del_provider;
 
        return 0;
 
-err_of_clk_unreg:
-       kfree(reset);
-err_alloc_reset:
+err_del_provider:
        of_clk_del_provider(node);
 err_clk_unreg:
        while (--i >= 0) {
@@ -149,3 +154,59 @@ err_clk_unreg:
        }
        return ret;
 }
+
+static void devm_sunxi_ccu_release(struct device *dev, void *res)
+{
+       struct sunxi_ccu *ccu = res;
+       const struct sunxi_ccu_desc *desc = ccu->desc;
+       int i;
+
+       reset_controller_unregister(&ccu->reset.rcdev);
+       of_clk_del_provider(dev->of_node);
+
+       for (i = 0; i < desc->hw_clks->num; i++) {
+               struct clk_hw *hw = desc->hw_clks->hws[i];
+
+               if (!hw)
+                       continue;
+               clk_hw_unregister(hw);
+       }
+}
+
+int devm_sunxi_ccu_probe(struct device *dev, void __iomem *reg,
+                        const struct sunxi_ccu_desc *desc)
+{
+       struct sunxi_ccu *ccu;
+       int ret;
+
+       ccu = devres_alloc(devm_sunxi_ccu_release, sizeof(*ccu), GFP_KERNEL);
+       if (!ccu)
+               return -ENOMEM;
+
+       ret = sunxi_ccu_probe(ccu, dev, dev->of_node, reg, desc);
+       if (ret) {
+               devres_free(ccu);
+               return ret;
+       }
+
+       devres_add(dev, ccu);
+
+       return 0;
+}
+
+void of_sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
+                       const struct sunxi_ccu_desc *desc)
+{
+       struct sunxi_ccu *ccu;
+       int ret;
+
+       ccu = kzalloc(sizeof(*ccu), GFP_KERNEL);
+       if (!ccu)
+               return;
+
+       ret = sunxi_ccu_probe(ccu, NULL, node, reg, desc);
+       if (ret) {
+               pr_err("%pOF: probing clocks failed: %d\n", node, ret);
+               kfree(ccu);
+       }
+}
index 04e7a12..98a1834 100644 (file)
@@ -63,7 +63,9 @@ struct ccu_pll_nb {
 
 int ccu_pll_notifier_register(struct ccu_pll_nb *pll_nb);
 
-int sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
-                   const struct sunxi_ccu_desc *desc);
+int devm_sunxi_ccu_probe(struct device *dev, void __iomem *reg,
+                        const struct sunxi_ccu_desc *desc);
+void of_sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
+                       const struct sunxi_ccu_desc *desc);
 
 #endif /* _COMMON_H_ */