Merge branches 'clk-range', 'clk-uniphier', 'clk-apple' and 'clk-qcom' into clk-next
[linux-2.6-microblaze.git] / drivers / clk / mediatek / clk-cpumux.c
index e188018..c11b3fa 100644 (file)
@@ -5,13 +5,24 @@
  */
 
 #include <linux/clk-provider.h>
+#include <linux/container_of.h>
+#include <linux/err.h>
 #include <linux/mfd/syscon.h>
 #include <linux/module.h>
+#include <linux/regmap.h>
 #include <linux/slab.h>
 
 #include "clk-mtk.h"
 #include "clk-cpumux.h"
 
+struct mtk_clk_cpumux {
+       struct clk_hw   hw;
+       struct regmap   *regmap;
+       u32             reg;
+       u32             mask;
+       u8              shift;
+};
+
 static inline struct mtk_clk_cpumux *to_mtk_clk_cpumux(struct clk_hw *_hw)
 {
        return container_of(_hw, struct mtk_clk_cpumux, hw);
@@ -77,6 +88,21 @@ mtk_clk_register_cpumux(const struct mtk_composite *mux,
        return clk;
 }
 
+static void mtk_clk_unregister_cpumux(struct clk *clk)
+{
+       struct mtk_clk_cpumux *cpumux;
+       struct clk_hw *hw;
+
+       hw = __clk_get_hw(clk);
+       if (!hw)
+               return;
+
+       cpumux = to_mtk_clk_cpumux(hw);
+
+       clk_unregister(clk);
+       kfree(cpumux);
+}
+
 int mtk_clk_register_cpumuxes(struct device_node *node,
                              const struct mtk_composite *clks, int num,
                              struct clk_onecell_data *clk_data)
@@ -87,25 +113,58 @@ int mtk_clk_register_cpumuxes(struct device_node *node,
 
        regmap = device_node_to_regmap(node);
        if (IS_ERR(regmap)) {
-               pr_err("Cannot find regmap for %pOF: %ld\n", node,
-                      PTR_ERR(regmap));
+               pr_err("Cannot find regmap for %pOF: %pe\n", node, regmap);
                return PTR_ERR(regmap);
        }
 
        for (i = 0; i < num; i++) {
                const struct mtk_composite *mux = &clks[i];
 
+               if (!IS_ERR_OR_NULL(clk_data->clks[mux->id])) {
+                       pr_warn("%pOF: Trying to register duplicate clock ID: %d\n",
+                               node, mux->id);
+                       continue;
+               }
+
                clk = mtk_clk_register_cpumux(mux, regmap);
                if (IS_ERR(clk)) {
-                       pr_err("Failed to register clk %s: %ld\n",
-                              mux->name, PTR_ERR(clk));
-                       continue;
+                       pr_err("Failed to register clk %s: %pe\n", mux->name, clk);
+                       goto err;
                }
 
                clk_data->clks[mux->id] = clk;
        }
 
        return 0;
+
+err:
+       while (--i >= 0) {
+               const struct mtk_composite *mux = &clks[i];
+
+               if (IS_ERR_OR_NULL(clk_data->clks[mux->id]))
+                       continue;
+
+               mtk_clk_unregister_cpumux(clk_data->clks[mux->id]);
+               clk_data->clks[mux->id] = ERR_PTR(-ENOENT);
+       }
+
+       return PTR_ERR(clk);
+}
+
+void mtk_clk_unregister_cpumuxes(const struct mtk_composite *clks, int num,
+                                struct clk_onecell_data *clk_data)
+{
+       int i;
+
+       for (i = num; i > 0; i--) {
+               const struct mtk_composite *mux = &clks[i - 1];
+
+               if (IS_ERR_OR_NULL(clk_data->clks[mux->id]))
+                       continue;
+
+               mtk_clk_unregister_cpumux(clk_data->clks[mux->id]);
+               clk_data->clks[mux->id] = ERR_PTR(-ENOENT);
+       }
 }
 
 MODULE_LICENSE("GPL");