clk: mediatek: pll: Implement unregister API
authorChen-Yu Tsai <wenst@chromium.org>
Tue, 8 Feb 2022 12:40:16 +0000 (20:40 +0800)
committerStephen Boyd <sboyd@kernel.org>
Thu, 17 Feb 2022 20:12:23 +0000 (12:12 -0800)
The PLL clk type within the MediaTek clk driver library only has a
register function, and no corresponding unregister function. This
means there is no way for its users to properly implement cleanup
and removal.

Add a matching unregister function for the PLL type clk.

Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
Reviewed-by: Miles Chen <miles.chen@mediatek.com>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Link: https://lore.kernel.org/r/20220208124034.414635-14-wenst@chromium.org
Reviewed-by: Chun-Jie Chen <chun-jie.chen@mediatek.com>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
drivers/clk/mediatek/clk-pll.c
drivers/clk/mediatek/clk-pll.h

index 64f59554bc9bd03f82dee58f07cc95f0a3b4c502..b54e33b75d4ee397a15b0e0ebcdbd2e379aac5ed 100644 (file)
@@ -360,6 +360,21 @@ static struct clk *mtk_clk_register_pll(const struct mtk_pll_data *data,
        return clk;
 }
 
+static void mtk_clk_unregister_pll(struct clk *clk)
+{
+       struct clk_hw *hw;
+       struct mtk_clk_pll *pll;
+
+       hw = __clk_get_hw(clk);
+       if (!hw)
+               return;
+
+       pll = to_mtk_clk_pll(hw);
+
+       clk_unregister(clk);
+       kfree(pll);
+}
+
 void mtk_clk_register_plls(struct device_node *node,
                const struct mtk_pll_data *plls, int num_plls, struct clk_onecell_data *clk_data)
 {
@@ -388,4 +403,44 @@ void mtk_clk_register_plls(struct device_node *node,
 }
 EXPORT_SYMBOL_GPL(mtk_clk_register_plls);
 
+static __iomem void *mtk_clk_pll_get_base(struct clk *clk,
+                                         const struct mtk_pll_data *data)
+{
+       struct clk_hw *hw = __clk_get_hw(clk);
+       struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
+
+       return pll->base_addr - data->reg;
+}
+
+void mtk_clk_unregister_plls(const struct mtk_pll_data *plls, int num_plls,
+                            struct clk_onecell_data *clk_data)
+{
+       __iomem void *base = NULL;
+       int i;
+
+       if (!clk_data)
+               return;
+
+       for (i = num_plls; i > 0; i--) {
+               const struct mtk_pll_data *pll = &plls[i - 1];
+
+               if (IS_ERR_OR_NULL(clk_data->clks[pll->id]))
+                       continue;
+
+               /*
+                * This is quite ugly but unfortunately the clks don't have
+                * any device tied to them, so there's no place to store the
+                * pointer to the I/O region base address. We have to fetch
+                * it from one of the registered clks.
+                */
+               base = mtk_clk_pll_get_base(clk_data->clks[pll->id], pll);
+
+               mtk_clk_unregister_pll(clk_data->clks[pll->id]);
+               clk_data->clks[pll->id] = ERR_PTR(-ENOENT);
+       }
+
+       iounmap(base);
+}
+EXPORT_SYMBOL_GPL(mtk_clk_unregister_plls);
+
 MODULE_LICENSE("GPL");
index d01b0c38311d489c361ba48121d14c6ec9ea5740..a889b1e472e7cdbb41d3aefdfc79d5a2f661449d 100644 (file)
@@ -51,5 +51,7 @@ struct mtk_pll_data {
 void mtk_clk_register_plls(struct device_node *node,
                           const struct mtk_pll_data *plls, int num_plls,
                           struct clk_onecell_data *clk_data);
+void mtk_clk_unregister_plls(const struct mtk_pll_data *plls, int num_plls,
+                            struct clk_onecell_data *clk_data);
 
 #endif /* __DRV_CLK_MTK_PLL_H */