Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v4.3 249 lines 6.1 kB view raw
1/* 2 * Copyright (c) 2015 Endless Mobile, Inc. 3 * Author: Carlo Caione <carlo@endlessm.com> 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms and conditions of the GNU General Public License, 7 * version 2, as published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 * more details. 13 * 14 * You should have received a copy of the GNU General Public License along with 15 * this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18#include <linux/clk-provider.h> 19#include <linux/mfd/syscon.h> 20#include <linux/slab.h> 21 22#include "clkc.h" 23 24static DEFINE_SPINLOCK(clk_lock); 25 26static struct clk **clks; 27static struct clk_onecell_data clk_data; 28 29struct clk ** __init meson_clk_init(struct device_node *np, 30 unsigned long nr_clks) 31{ 32 clks = kcalloc(nr_clks, sizeof(*clks), GFP_KERNEL); 33 if (!clks) 34 return ERR_PTR(-ENOMEM); 35 36 clk_data.clks = clks; 37 clk_data.clk_num = nr_clks; 38 of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); 39 40 return clks; 41} 42 43static void meson_clk_add_lookup(struct clk *clk, unsigned int id) 44{ 45 if (clks && id) 46 clks[id] = clk; 47} 48 49static struct clk * __init 50meson_clk_register_composite(const struct clk_conf *clk_conf, 51 void __iomem *clk_base) 52{ 53 struct clk *clk; 54 struct clk_mux *mux = NULL; 55 struct clk_divider *div = NULL; 56 struct clk_gate *gate = NULL; 57 const struct clk_ops *mux_ops = NULL; 58 const struct composite_conf *composite_conf; 59 60 composite_conf = clk_conf->conf.composite; 61 62 if (clk_conf->num_parents > 1) { 63 mux = kzalloc(sizeof(*mux), GFP_KERNEL); 64 if (!mux) 65 return ERR_PTR(-ENOMEM); 66 67 mux->reg = clk_base + clk_conf->reg_off 68 + composite_conf->mux_parm.reg_off; 69 mux->shift = composite_conf->mux_parm.shift; 70 mux->mask = BIT(composite_conf->mux_parm.width) - 1; 71 mux->flags = composite_conf->mux_flags; 72 mux->lock = &clk_lock; 73 mux->table = composite_conf->mux_table; 74 mux_ops = (composite_conf->mux_flags & CLK_MUX_READ_ONLY) ? 75 &clk_mux_ro_ops : &clk_mux_ops; 76 } 77 78 if (MESON_PARM_APPLICABLE(&composite_conf->div_parm)) { 79 div = kzalloc(sizeof(*div), GFP_KERNEL); 80 if (!div) { 81 clk = ERR_PTR(-ENOMEM); 82 goto error; 83 } 84 85 div->reg = clk_base + clk_conf->reg_off 86 + composite_conf->div_parm.reg_off; 87 div->shift = composite_conf->div_parm.shift; 88 div->width = composite_conf->div_parm.width; 89 div->lock = &clk_lock; 90 div->flags = composite_conf->div_flags; 91 div->table = composite_conf->div_table; 92 } 93 94 if (MESON_PARM_APPLICABLE(&composite_conf->gate_parm)) { 95 gate = kzalloc(sizeof(*gate), GFP_KERNEL); 96 if (!gate) { 97 clk = ERR_PTR(-ENOMEM); 98 goto error; 99 } 100 101 gate->reg = clk_base + clk_conf->reg_off 102 + composite_conf->div_parm.reg_off; 103 gate->bit_idx = composite_conf->gate_parm.shift; 104 gate->flags = composite_conf->gate_flags; 105 gate->lock = &clk_lock; 106 } 107 108 clk = clk_register_composite(NULL, clk_conf->clk_name, 109 clk_conf->clks_parent, 110 clk_conf->num_parents, 111 mux ? &mux->hw : NULL, mux_ops, 112 div ? &div->hw : NULL, &clk_divider_ops, 113 gate ? &gate->hw : NULL, &clk_gate_ops, 114 clk_conf->flags); 115 if (IS_ERR(clk)) 116 goto error; 117 118 return clk; 119 120error: 121 kfree(gate); 122 kfree(div); 123 kfree(mux); 124 125 return clk; 126} 127 128static struct clk * __init 129meson_clk_register_fixed_factor(const struct clk_conf *clk_conf, 130 void __iomem *clk_base) 131{ 132 struct clk *clk; 133 const struct fixed_fact_conf *fixed_fact_conf; 134 const struct parm *p; 135 unsigned int mult, div; 136 u32 reg; 137 138 fixed_fact_conf = &clk_conf->conf.fixed_fact; 139 140 mult = clk_conf->conf.fixed_fact.mult; 141 div = clk_conf->conf.fixed_fact.div; 142 143 if (!mult) { 144 mult = 1; 145 p = &fixed_fact_conf->mult_parm; 146 if (MESON_PARM_APPLICABLE(p)) { 147 reg = readl(clk_base + clk_conf->reg_off + p->reg_off); 148 mult = PARM_GET(p->width, p->shift, reg); 149 } 150 } 151 152 if (!div) { 153 div = 1; 154 p = &fixed_fact_conf->div_parm; 155 if (MESON_PARM_APPLICABLE(p)) { 156 reg = readl(clk_base + clk_conf->reg_off + p->reg_off); 157 mult = PARM_GET(p->width, p->shift, reg); 158 } 159 } 160 161 clk = clk_register_fixed_factor(NULL, 162 clk_conf->clk_name, 163 clk_conf->clks_parent[0], 164 clk_conf->flags, 165 mult, div); 166 167 return clk; 168} 169 170static struct clk * __init 171meson_clk_register_fixed_rate(const struct clk_conf *clk_conf, 172 void __iomem *clk_base) 173{ 174 struct clk *clk; 175 const struct fixed_rate_conf *fixed_rate_conf; 176 const struct parm *r; 177 unsigned long rate; 178 u32 reg; 179 180 fixed_rate_conf = &clk_conf->conf.fixed_rate; 181 rate = fixed_rate_conf->rate; 182 183 if (!rate) { 184 r = &fixed_rate_conf->rate_parm; 185 reg = readl(clk_base + clk_conf->reg_off + r->reg_off); 186 rate = PARM_GET(r->width, r->shift, reg); 187 } 188 189 rate *= 1000000; 190 191 clk = clk_register_fixed_rate(NULL, 192 clk_conf->clk_name, 193 clk_conf->num_parents 194 ? clk_conf->clks_parent[0] : NULL, 195 clk_conf->flags, rate); 196 197 return clk; 198} 199 200void __init meson_clk_register_clks(const struct clk_conf *clk_confs, 201 size_t nr_confs, 202 void __iomem *clk_base) 203{ 204 unsigned int i; 205 struct clk *clk = NULL; 206 207 for (i = 0; i < nr_confs; i++) { 208 const struct clk_conf *clk_conf = &clk_confs[i]; 209 210 switch (clk_conf->clk_type) { 211 case CLK_FIXED_RATE: 212 clk = meson_clk_register_fixed_rate(clk_conf, 213 clk_base); 214 break; 215 case CLK_FIXED_FACTOR: 216 clk = meson_clk_register_fixed_factor(clk_conf, 217 clk_base); 218 break; 219 case CLK_COMPOSITE: 220 clk = meson_clk_register_composite(clk_conf, 221 clk_base); 222 break; 223 case CLK_CPU: 224 clk = meson_clk_register_cpu(clk_conf, clk_base, 225 &clk_lock); 226 break; 227 case CLK_PLL: 228 clk = meson_clk_register_pll(clk_conf, clk_base, 229 &clk_lock); 230 break; 231 default: 232 clk = NULL; 233 } 234 235 if (!clk) { 236 pr_err("%s: unknown clock type %d\n", __func__, 237 clk_conf->clk_type); 238 continue; 239 } 240 241 if (IS_ERR(clk)) { 242 pr_warn("%s: Unable to create %s clock\n", __func__, 243 clk_conf->clk_name); 244 continue; 245 } 246 247 meson_clk_add_lookup(clk, clk_conf->clk_id); 248 } 249}