These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / clk / meson / clkc.c
diff --git a/kernel/drivers/clk/meson/clkc.c b/kernel/drivers/clk/meson/clkc.c
new file mode 100644 (file)
index 0000000..c83ae13
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2015 Endless Mobile, Inc.
+ * Author: Carlo Caione <carlo@endlessm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/mfd/syscon.h>
+#include <linux/slab.h>
+
+#include "clkc.h"
+
+static DEFINE_SPINLOCK(clk_lock);
+
+static struct clk **clks;
+static struct clk_onecell_data clk_data;
+
+struct clk ** __init meson_clk_init(struct device_node *np,
+                                  unsigned long nr_clks)
+{
+       clks = kcalloc(nr_clks, sizeof(*clks), GFP_KERNEL);
+       if (!clks)
+               return ERR_PTR(-ENOMEM);
+
+       clk_data.clks = clks;
+       clk_data.clk_num = nr_clks;
+       of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+
+       return clks;
+}
+
+static void meson_clk_add_lookup(struct clk *clk, unsigned int id)
+{
+       if (clks && id)
+               clks[id] = clk;
+}
+
+static struct clk * __init
+meson_clk_register_composite(const struct clk_conf *clk_conf,
+                            void __iomem *clk_base)
+{
+       struct clk *clk;
+       struct clk_mux *mux = NULL;
+       struct clk_divider *div = NULL;
+       struct clk_gate *gate = NULL;
+       const struct clk_ops *mux_ops = NULL;
+       const struct composite_conf *composite_conf;
+
+       composite_conf = clk_conf->conf.composite;
+
+       if (clk_conf->num_parents > 1) {
+               mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+               if (!mux)
+                       return ERR_PTR(-ENOMEM);
+
+               mux->reg = clk_base + clk_conf->reg_off
+                               + composite_conf->mux_parm.reg_off;
+               mux->shift = composite_conf->mux_parm.shift;
+               mux->mask = BIT(composite_conf->mux_parm.width) - 1;
+               mux->flags = composite_conf->mux_flags;
+               mux->lock = &clk_lock;
+               mux->table = composite_conf->mux_table;
+               mux_ops = (composite_conf->mux_flags & CLK_MUX_READ_ONLY) ?
+                         &clk_mux_ro_ops : &clk_mux_ops;
+       }
+
+       if (MESON_PARM_APPLICABLE(&composite_conf->div_parm)) {
+               div = kzalloc(sizeof(*div), GFP_KERNEL);
+               if (!div) {
+                       clk = ERR_PTR(-ENOMEM);
+                       goto error;
+               }
+
+               div->reg = clk_base + clk_conf->reg_off
+                               + composite_conf->div_parm.reg_off;
+               div->shift = composite_conf->div_parm.shift;
+               div->width = composite_conf->div_parm.width;
+               div->lock = &clk_lock;
+               div->flags = composite_conf->div_flags;
+               div->table = composite_conf->div_table;
+       }
+
+       if (MESON_PARM_APPLICABLE(&composite_conf->gate_parm)) {
+               gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+               if (!gate) {
+                       clk = ERR_PTR(-ENOMEM);
+                       goto error;
+               }
+
+               gate->reg = clk_base + clk_conf->reg_off
+                               + composite_conf->div_parm.reg_off;
+               gate->bit_idx = composite_conf->gate_parm.shift;
+               gate->flags = composite_conf->gate_flags;
+               gate->lock = &clk_lock;
+       }
+
+       clk = clk_register_composite(NULL, clk_conf->clk_name,
+                                   clk_conf->clks_parent,
+                                   clk_conf->num_parents,
+                                   mux ? &mux->hw : NULL, mux_ops,
+                                   div ? &div->hw : NULL, &clk_divider_ops,
+                                   gate ? &gate->hw : NULL, &clk_gate_ops,
+                                   clk_conf->flags);
+       if (IS_ERR(clk))
+               goto error;
+
+       return clk;
+
+error:
+       kfree(gate);
+       kfree(div);
+       kfree(mux);
+
+       return clk;
+}
+
+static struct clk * __init
+meson_clk_register_fixed_factor(const struct clk_conf *clk_conf,
+                               void __iomem *clk_base)
+{
+       struct clk *clk;
+       const struct fixed_fact_conf *fixed_fact_conf;
+       const struct parm *p;
+       unsigned int mult, div;
+       u32 reg;
+
+       fixed_fact_conf = &clk_conf->conf.fixed_fact;
+
+       mult = clk_conf->conf.fixed_fact.mult;
+       div = clk_conf->conf.fixed_fact.div;
+
+       if (!mult) {
+               mult = 1;
+               p = &fixed_fact_conf->mult_parm;
+               if (MESON_PARM_APPLICABLE(p)) {
+                       reg = readl(clk_base + clk_conf->reg_off + p->reg_off);
+                       mult = PARM_GET(p->width, p->shift, reg);
+               }
+       }
+
+       if (!div) {
+               div = 1;
+               p = &fixed_fact_conf->div_parm;
+               if (MESON_PARM_APPLICABLE(p)) {
+                       reg = readl(clk_base + clk_conf->reg_off + p->reg_off);
+                       mult = PARM_GET(p->width, p->shift, reg);
+               }
+       }
+
+       clk = clk_register_fixed_factor(NULL,
+                       clk_conf->clk_name,
+                       clk_conf->clks_parent[0],
+                       clk_conf->flags,
+                       mult, div);
+
+       return clk;
+}
+
+static struct clk * __init
+meson_clk_register_fixed_rate(const struct clk_conf *clk_conf,
+                             void __iomem *clk_base)
+{
+       struct clk *clk;
+       const struct fixed_rate_conf *fixed_rate_conf;
+       const struct parm *r;
+       unsigned long rate;
+       u32 reg;
+
+       fixed_rate_conf = &clk_conf->conf.fixed_rate;
+       rate = fixed_rate_conf->rate;
+
+       if (!rate) {
+               r = &fixed_rate_conf->rate_parm;
+               reg = readl(clk_base + clk_conf->reg_off + r->reg_off);
+               rate = PARM_GET(r->width, r->shift, reg);
+       }
+
+       rate *= 1000000;
+
+       clk = clk_register_fixed_rate(NULL,
+                       clk_conf->clk_name,
+                       clk_conf->num_parents
+                               ? clk_conf->clks_parent[0] : NULL,
+                       clk_conf->flags, rate);
+
+       return clk;
+}
+
+void __init meson_clk_register_clks(const struct clk_conf *clk_confs,
+                                   size_t nr_confs,
+                                   void __iomem *clk_base)
+{
+       unsigned int i;
+       struct clk *clk = NULL;
+
+       for (i = 0; i < nr_confs; i++) {
+               const struct clk_conf *clk_conf = &clk_confs[i];
+
+               switch (clk_conf->clk_type) {
+               case CLK_FIXED_RATE:
+                       clk = meson_clk_register_fixed_rate(clk_conf,
+                                                           clk_base);
+                       break;
+               case CLK_FIXED_FACTOR:
+                       clk = meson_clk_register_fixed_factor(clk_conf,
+                                                             clk_base);
+                       break;
+               case CLK_COMPOSITE:
+                       clk = meson_clk_register_composite(clk_conf,
+                                                          clk_base);
+                       break;
+               case CLK_CPU:
+                       clk = meson_clk_register_cpu(clk_conf, clk_base,
+                                                    &clk_lock);
+                       break;
+               case CLK_PLL:
+                       clk = meson_clk_register_pll(clk_conf, clk_base,
+                                                    &clk_lock);
+                       break;
+               default:
+                       clk = NULL;
+               }
+
+               if (!clk) {
+                       pr_err("%s: unknown clock type %d\n", __func__,
+                              clk_conf->clk_type);
+                       continue;
+               }
+
+               if (IS_ERR(clk)) {
+                       pr_warn("%s: Unable to create %s clock\n", __func__,
+                               clk_conf->clk_name);
+                       continue;
+               }
+
+               meson_clk_add_lookup(clk, clk_conf->clk_id);
+       }
+}