Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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}