1 /*
2 * Copyright (c) 2013 Samsung Electronics Co., Ltd.
3 * Copyright (c) 2013 Linaro Ltd.
4 * Author: Thomas Abraham <thomas.ab@samsung.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * This file includes utility functions to register clocks to common
11 * clock framework for Samsung platforms.
12 */
13
14 #include <linux/syscore_ops.h>
15 #include "clk.h"
16
samsung_clk_save(void __iomem * base,struct samsung_clk_reg_dump * rd,unsigned int num_regs)17 void samsung_clk_save(void __iomem *base,
18 struct samsung_clk_reg_dump *rd,
19 unsigned int num_regs)
20 {
21 for (; num_regs > 0; --num_regs, ++rd)
22 rd->value = readl(base + rd->offset);
23 }
24
samsung_clk_restore(void __iomem * base,const struct samsung_clk_reg_dump * rd,unsigned int num_regs)25 void samsung_clk_restore(void __iomem *base,
26 const struct samsung_clk_reg_dump *rd,
27 unsigned int num_regs)
28 {
29 for (; num_regs > 0; --num_regs, ++rd)
30 writel(rd->value, base + rd->offset);
31 }
32
samsung_clk_alloc_reg_dump(const unsigned long * rdump,unsigned long nr_rdump)33 struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump(
34 const unsigned long *rdump,
35 unsigned long nr_rdump)
36 {
37 struct samsung_clk_reg_dump *rd;
38 unsigned int i;
39
40 rd = kcalloc(nr_rdump, sizeof(*rd), GFP_KERNEL);
41 if (!rd)
42 return NULL;
43
44 for (i = 0; i < nr_rdump; ++i)
45 rd[i].offset = rdump[i];
46
47 return rd;
48 }
49
50 /* setup the essentials required to support clock lookup using ccf */
samsung_clk_init(struct device_node * np,void __iomem * base,unsigned long nr_clks)51 struct samsung_clk_provider *__init samsung_clk_init(struct device_node *np,
52 void __iomem *base, unsigned long nr_clks)
53 {
54 struct samsung_clk_provider *ctx;
55 struct clk **clk_table;
56 int i;
57
58 ctx = kzalloc(sizeof(struct samsung_clk_provider), GFP_KERNEL);
59 if (!ctx)
60 panic("could not allocate clock provider context.\n");
61
62 clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL);
63 if (!clk_table)
64 panic("could not allocate clock lookup table\n");
65
66 for (i = 0; i < nr_clks; ++i)
67 clk_table[i] = ERR_PTR(-ENOENT);
68
69 ctx->reg_base = base;
70 ctx->clk_data.clks = clk_table;
71 ctx->clk_data.clk_num = nr_clks;
72 spin_lock_init(&ctx->lock);
73
74 return ctx;
75 }
76
samsung_clk_of_add_provider(struct device_node * np,struct samsung_clk_provider * ctx)77 void __init samsung_clk_of_add_provider(struct device_node *np,
78 struct samsung_clk_provider *ctx)
79 {
80 if (np) {
81 if (of_clk_add_provider(np, of_clk_src_onecell_get,
82 &ctx->clk_data))
83 panic("could not register clk provider\n");
84 }
85 }
86
87 /* add a clock instance to the clock lookup table used for dt based lookup */
samsung_clk_add_lookup(struct samsung_clk_provider * ctx,struct clk * clk,unsigned int id)88 void samsung_clk_add_lookup(struct samsung_clk_provider *ctx, struct clk *clk,
89 unsigned int id)
90 {
91 if (ctx->clk_data.clks && id)
92 ctx->clk_data.clks[id] = clk;
93 }
94
95 /* register a list of aliases */
samsung_clk_register_alias(struct samsung_clk_provider * ctx,struct samsung_clock_alias * list,unsigned int nr_clk)96 void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx,
97 struct samsung_clock_alias *list,
98 unsigned int nr_clk)
99 {
100 struct clk *clk;
101 unsigned int idx, ret;
102
103 if (!ctx->clk_data.clks) {
104 pr_err("%s: clock table missing\n", __func__);
105 return;
106 }
107
108 for (idx = 0; idx < nr_clk; idx++, list++) {
109 if (!list->id) {
110 pr_err("%s: clock id missing for index %d\n", __func__,
111 idx);
112 continue;
113 }
114
115 clk = ctx->clk_data.clks[list->id];
116 if (!clk) {
117 pr_err("%s: failed to find clock %d\n", __func__,
118 list->id);
119 continue;
120 }
121
122 ret = clk_register_clkdev(clk, list->alias, list->dev_name);
123 if (ret)
124 pr_err("%s: failed to register lookup %s\n",
125 __func__, list->alias);
126 }
127 }
128
129 /* register a list of fixed clocks */
samsung_clk_register_fixed_rate(struct samsung_clk_provider * ctx,struct samsung_fixed_rate_clock * list,unsigned int nr_clk)130 void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx,
131 struct samsung_fixed_rate_clock *list, unsigned int nr_clk)
132 {
133 struct clk *clk;
134 unsigned int idx, ret;
135
136 for (idx = 0; idx < nr_clk; idx++, list++) {
137 clk = clk_register_fixed_rate(NULL, list->name,
138 list->parent_name, list->flags, list->fixed_rate);
139 if (IS_ERR(clk)) {
140 pr_err("%s: failed to register clock %s\n", __func__,
141 list->name);
142 continue;
143 }
144
145 samsung_clk_add_lookup(ctx, clk, list->id);
146
147 /*
148 * Unconditionally add a clock lookup for the fixed rate clocks.
149 * There are not many of these on any of Samsung platforms.
150 */
151 ret = clk_register_clkdev(clk, list->name, NULL);
152 if (ret)
153 pr_err("%s: failed to register clock lookup for %s",
154 __func__, list->name);
155 }
156 }
157
158 /* register a list of fixed factor clocks */
samsung_clk_register_fixed_factor(struct samsung_clk_provider * ctx,struct samsung_fixed_factor_clock * list,unsigned int nr_clk)159 void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx,
160 struct samsung_fixed_factor_clock *list, unsigned int nr_clk)
161 {
162 struct clk *clk;
163 unsigned int idx;
164
165 for (idx = 0; idx < nr_clk; idx++, list++) {
166 clk = clk_register_fixed_factor(NULL, list->name,
167 list->parent_name, list->flags, list->mult, list->div);
168 if (IS_ERR(clk)) {
169 pr_err("%s: failed to register clock %s\n", __func__,
170 list->name);
171 continue;
172 }
173
174 samsung_clk_add_lookup(ctx, clk, list->id);
175 }
176 }
177
178 /* register a list of mux clocks */
samsung_clk_register_mux(struct samsung_clk_provider * ctx,struct samsung_mux_clock * list,unsigned int nr_clk)179 void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx,
180 struct samsung_mux_clock *list,
181 unsigned int nr_clk)
182 {
183 struct clk *clk;
184 unsigned int idx, ret;
185
186 for (idx = 0; idx < nr_clk; idx++, list++) {
187 clk = clk_register_mux(NULL, list->name, list->parent_names,
188 list->num_parents, list->flags,
189 ctx->reg_base + list->offset,
190 list->shift, list->width, list->mux_flags, &ctx->lock);
191 if (IS_ERR(clk)) {
192 pr_err("%s: failed to register clock %s\n", __func__,
193 list->name);
194 continue;
195 }
196
197 samsung_clk_add_lookup(ctx, clk, list->id);
198
199 /* register a clock lookup only if a clock alias is specified */
200 if (list->alias) {
201 ret = clk_register_clkdev(clk, list->alias,
202 list->dev_name);
203 if (ret)
204 pr_err("%s: failed to register lookup %s\n",
205 __func__, list->alias);
206 }
207 }
208 }
209
210 /* register a list of div clocks */
samsung_clk_register_div(struct samsung_clk_provider * ctx,struct samsung_div_clock * list,unsigned int nr_clk)211 void __init samsung_clk_register_div(struct samsung_clk_provider *ctx,
212 struct samsung_div_clock *list,
213 unsigned int nr_clk)
214 {
215 struct clk *clk;
216 unsigned int idx, ret;
217
218 for (idx = 0; idx < nr_clk; idx++, list++) {
219 if (list->table)
220 clk = clk_register_divider_table(NULL, list->name,
221 list->parent_name, list->flags,
222 ctx->reg_base + list->offset,
223 list->shift, list->width, list->div_flags,
224 list->table, &ctx->lock);
225 else
226 clk = clk_register_divider(NULL, list->name,
227 list->parent_name, list->flags,
228 ctx->reg_base + list->offset, list->shift,
229 list->width, list->div_flags, &ctx->lock);
230 if (IS_ERR(clk)) {
231 pr_err("%s: failed to register clock %s\n", __func__,
232 list->name);
233 continue;
234 }
235
236 samsung_clk_add_lookup(ctx, clk, list->id);
237
238 /* register a clock lookup only if a clock alias is specified */
239 if (list->alias) {
240 ret = clk_register_clkdev(clk, list->alias,
241 list->dev_name);
242 if (ret)
243 pr_err("%s: failed to register lookup %s\n",
244 __func__, list->alias);
245 }
246 }
247 }
248
249 /* register a list of gate clocks */
samsung_clk_register_gate(struct samsung_clk_provider * ctx,struct samsung_gate_clock * list,unsigned int nr_clk)250 void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx,
251 struct samsung_gate_clock *list,
252 unsigned int nr_clk)
253 {
254 struct clk *clk;
255 unsigned int idx, ret;
256
257 for (idx = 0; idx < nr_clk; idx++, list++) {
258 clk = clk_register_gate(NULL, list->name, list->parent_name,
259 list->flags, ctx->reg_base + list->offset,
260 list->bit_idx, list->gate_flags, &ctx->lock);
261 if (IS_ERR(clk)) {
262 pr_err("%s: failed to register clock %s\n", __func__,
263 list->name);
264 continue;
265 }
266
267 /* register a clock lookup only if a clock alias is specified */
268 if (list->alias) {
269 ret = clk_register_clkdev(clk, list->alias,
270 list->dev_name);
271 if (ret)
272 pr_err("%s: failed to register lookup %s\n",
273 __func__, list->alias);
274 }
275
276 samsung_clk_add_lookup(ctx, clk, list->id);
277 }
278 }
279
280 /*
281 * obtain the clock speed of all external fixed clock sources from device
282 * tree and register it
283 */
284 #ifdef CONFIG_OF
samsung_clk_of_register_fixed_ext(struct samsung_clk_provider * ctx,struct samsung_fixed_rate_clock * fixed_rate_clk,unsigned int nr_fixed_rate_clk,const struct of_device_id * clk_matches)285 void __init samsung_clk_of_register_fixed_ext(struct samsung_clk_provider *ctx,
286 struct samsung_fixed_rate_clock *fixed_rate_clk,
287 unsigned int nr_fixed_rate_clk,
288 const struct of_device_id *clk_matches)
289 {
290 const struct of_device_id *match;
291 struct device_node *clk_np;
292 u32 freq;
293
294 for_each_matching_node_and_match(clk_np, clk_matches, &match) {
295 if (of_property_read_u32(clk_np, "clock-frequency", &freq))
296 continue;
297 fixed_rate_clk[(unsigned long)match->data].fixed_rate = freq;
298 }
299 samsung_clk_register_fixed_rate(ctx, fixed_rate_clk, nr_fixed_rate_clk);
300 }
301 #endif
302
303 /* utility function to get the rate of a specified clock */
_get_rate(const char * clk_name)304 unsigned long _get_rate(const char *clk_name)
305 {
306 struct clk *clk;
307
308 clk = __clk_lookup(clk_name);
309 if (!clk) {
310 pr_err("%s: could not find clock %s\n", __func__, clk_name);
311 return 0;
312 }
313
314 return clk_get_rate(clk);
315 }
316