Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.2-rc8 250 lines 6.2 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.h> 19#include <linux/clk-provider.h> 20#include <linux/mfd/syscon.h> 21#include <linux/slab.h> 22 23#include "clkc.h" 24 25static DEFINE_SPINLOCK(clk_lock); 26 27static struct clk **clks; 28static struct clk_onecell_data clk_data; 29 30struct clk ** __init meson_clk_init(struct device_node *np, 31 unsigned long nr_clks) 32{ 33 clks = kcalloc(nr_clks, sizeof(*clks), GFP_KERNEL); 34 if (!clks) 35 return ERR_PTR(-ENOMEM); 36 37 clk_data.clks = clks; 38 clk_data.clk_num = nr_clks; 39 of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); 40 41 return clks; 42} 43 44static void meson_clk_add_lookup(struct clk *clk, unsigned int id) 45{ 46 if (clks && id) 47 clks[id] = clk; 48} 49 50static struct clk * __init 51meson_clk_register_composite(const struct clk_conf *clk_conf, 52 void __iomem *clk_base) 53{ 54 struct clk *clk; 55 struct clk_mux *mux = NULL; 56 struct clk_divider *div = NULL; 57 struct clk_gate *gate = NULL; 58 const struct clk_ops *mux_ops = NULL; 59 const struct composite_conf *composite_conf; 60 61 composite_conf = clk_conf->conf.composite; 62 63 if (clk_conf->num_parents > 1) { 64 mux = kzalloc(sizeof(*mux), GFP_KERNEL); 65 if (!mux) 66 return ERR_PTR(-ENOMEM); 67 68 mux->reg = clk_base + clk_conf->reg_off 69 + composite_conf->mux_parm.reg_off; 70 mux->shift = composite_conf->mux_parm.shift; 71 mux->mask = BIT(composite_conf->mux_parm.width) - 1; 72 mux->flags = composite_conf->mux_flags; 73 mux->lock = &clk_lock; 74 mux->table = composite_conf->mux_table; 75 mux_ops = (composite_conf->mux_flags & CLK_MUX_READ_ONLY) ? 76 &clk_mux_ro_ops : &clk_mux_ops; 77 } 78 79 if (MESON_PARM_APPLICABLE(&composite_conf->div_parm)) { 80 div = kzalloc(sizeof(*div), GFP_KERNEL); 81 if (!div) { 82 clk = ERR_PTR(-ENOMEM); 83 goto error; 84 } 85 86 div->reg = clk_base + clk_conf->reg_off 87 + composite_conf->div_parm.reg_off; 88 div->shift = composite_conf->div_parm.shift; 89 div->width = composite_conf->div_parm.width; 90 div->lock = &clk_lock; 91 div->flags = composite_conf->div_flags; 92 div->table = composite_conf->div_table; 93 } 94 95 if (MESON_PARM_APPLICABLE(&composite_conf->gate_parm)) { 96 gate = kzalloc(sizeof(*gate), GFP_KERNEL); 97 if (!gate) { 98 clk = ERR_PTR(-ENOMEM); 99 goto error; 100 } 101 102 gate->reg = clk_base + clk_conf->reg_off 103 + composite_conf->div_parm.reg_off; 104 gate->bit_idx = composite_conf->gate_parm.shift; 105 gate->flags = composite_conf->gate_flags; 106 gate->lock = &clk_lock; 107 } 108 109 clk = clk_register_composite(NULL, clk_conf->clk_name, 110 clk_conf->clks_parent, 111 clk_conf->num_parents, 112 mux ? &mux->hw : NULL, mux_ops, 113 div ? &div->hw : NULL, &clk_divider_ops, 114 gate ? &gate->hw : NULL, &clk_gate_ops, 115 clk_conf->flags); 116 if (IS_ERR(clk)) 117 goto error; 118 119 return clk; 120 121error: 122 kfree(gate); 123 kfree(div); 124 kfree(mux); 125 126 return clk; 127} 128 129static struct clk * __init 130meson_clk_register_fixed_factor(const struct clk_conf *clk_conf, 131 void __iomem *clk_base) 132{ 133 struct clk *clk; 134 const struct fixed_fact_conf *fixed_fact_conf; 135 const struct parm *p; 136 unsigned int mult, div; 137 u32 reg; 138 139 fixed_fact_conf = &clk_conf->conf.fixed_fact; 140 141 mult = clk_conf->conf.fixed_fact.mult; 142 div = clk_conf->conf.fixed_fact.div; 143 144 if (!mult) { 145 mult = 1; 146 p = &fixed_fact_conf->mult_parm; 147 if (MESON_PARM_APPLICABLE(p)) { 148 reg = readl(clk_base + clk_conf->reg_off + p->reg_off); 149 mult = PARM_GET(p->width, p->shift, reg); 150 } 151 } 152 153 if (!div) { 154 div = 1; 155 p = &fixed_fact_conf->div_parm; 156 if (MESON_PARM_APPLICABLE(p)) { 157 reg = readl(clk_base + clk_conf->reg_off + p->reg_off); 158 mult = PARM_GET(p->width, p->shift, reg); 159 } 160 } 161 162 clk = clk_register_fixed_factor(NULL, 163 clk_conf->clk_name, 164 clk_conf->clks_parent[0], 165 clk_conf->flags, 166 mult, div); 167 168 return clk; 169} 170 171static struct clk * __init 172meson_clk_register_fixed_rate(const struct clk_conf *clk_conf, 173 void __iomem *clk_base) 174{ 175 struct clk *clk; 176 const struct fixed_rate_conf *fixed_rate_conf; 177 const struct parm *r; 178 unsigned long rate; 179 u32 reg; 180 181 fixed_rate_conf = &clk_conf->conf.fixed_rate; 182 rate = fixed_rate_conf->rate; 183 184 if (!rate) { 185 r = &fixed_rate_conf->rate_parm; 186 reg = readl(clk_base + clk_conf->reg_off + r->reg_off); 187 rate = PARM_GET(r->width, r->shift, reg); 188 } 189 190 rate *= 1000000; 191 192 clk = clk_register_fixed_rate(NULL, 193 clk_conf->clk_name, 194 clk_conf->num_parents 195 ? clk_conf->clks_parent[0] : NULL, 196 clk_conf->flags, rate); 197 198 return clk; 199} 200 201void __init meson_clk_register_clks(const struct clk_conf *clk_confs, 202 size_t nr_confs, 203 void __iomem *clk_base) 204{ 205 unsigned int i; 206 struct clk *clk = NULL; 207 208 for (i = 0; i < nr_confs; i++) { 209 const struct clk_conf *clk_conf = &clk_confs[i]; 210 211 switch (clk_conf->clk_type) { 212 case CLK_FIXED_RATE: 213 clk = meson_clk_register_fixed_rate(clk_conf, 214 clk_base); 215 break; 216 case CLK_FIXED_FACTOR: 217 clk = meson_clk_register_fixed_factor(clk_conf, 218 clk_base); 219 break; 220 case CLK_COMPOSITE: 221 clk = meson_clk_register_composite(clk_conf, 222 clk_base); 223 break; 224 case CLK_CPU: 225 clk = meson_clk_register_cpu(clk_conf, clk_base, 226 &clk_lock); 227 break; 228 case CLK_PLL: 229 clk = meson_clk_register_pll(clk_conf, clk_base, 230 &clk_lock); 231 break; 232 default: 233 clk = NULL; 234 } 235 236 if (!clk) { 237 pr_err("%s: unknown clock type %d\n", __func__, 238 clk_conf->clk_type); 239 continue; 240 } 241 242 if (IS_ERR(clk)) { 243 pr_warn("%s: Unable to create %s clock\n", __func__, 244 clk_conf->clk_name); 245 continue; 246 } 247 248 meson_clk_add_lookup(clk, clk_conf->clk_id); 249 } 250}