blob: c83ae1367abc1791618b858fe3544211c12dfaaf [file] [log] [blame]
Carlo Caione7a29a862015-06-01 13:13:53 +02001/*
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
Carlo Caione7a29a862015-06-01 13:13:53 +020018#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}