xref: /openbmc/linux/drivers/clk/samsung/clk.c (revision 3821a065)
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/slab.h>
15 #include <linux/clkdev.h>
16 #include <linux/clk.h>
17 #include <linux/clk-provider.h>
18 #include <linux/of_address.h>
19 #include <linux/syscore_ops.h>
20 
21 #include "clk.h"
22 
23 static LIST_HEAD(clock_reg_cache_list);
24 
25 void samsung_clk_save(void __iomem *base,
26 				    struct samsung_clk_reg_dump *rd,
27 				    unsigned int num_regs)
28 {
29 	for (; num_regs > 0; --num_regs, ++rd)
30 		rd->value = readl(base + rd->offset);
31 }
32 
33 void samsung_clk_restore(void __iomem *base,
34 				      const struct samsung_clk_reg_dump *rd,
35 				      unsigned int num_regs)
36 {
37 	for (; num_regs > 0; --num_regs, ++rd)
38 		writel(rd->value, base + rd->offset);
39 }
40 
41 struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump(
42 						const unsigned long *rdump,
43 						unsigned long nr_rdump)
44 {
45 	struct samsung_clk_reg_dump *rd;
46 	unsigned int i;
47 
48 	rd = kcalloc(nr_rdump, sizeof(*rd), GFP_KERNEL);
49 	if (!rd)
50 		return NULL;
51 
52 	for (i = 0; i < nr_rdump; ++i)
53 		rd[i].offset = rdump[i];
54 
55 	return rd;
56 }
57 
58 /* setup the essentials required to support clock lookup using ccf */
59 struct samsung_clk_provider *__init samsung_clk_init(struct device_node *np,
60 			void __iomem *base, unsigned long nr_clks)
61 {
62 	struct samsung_clk_provider *ctx;
63 	struct clk **clk_table;
64 	int i;
65 
66 	ctx = kzalloc(sizeof(struct samsung_clk_provider), GFP_KERNEL);
67 	if (!ctx)
68 		panic("could not allocate clock provider context.\n");
69 
70 	clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL);
71 	if (!clk_table)
72 		panic("could not allocate clock lookup table\n");
73 
74 	for (i = 0; i < nr_clks; ++i)
75 		clk_table[i] = ERR_PTR(-ENOENT);
76 
77 	ctx->reg_base = base;
78 	ctx->clk_data.clks = clk_table;
79 	ctx->clk_data.clk_num = nr_clks;
80 	spin_lock_init(&ctx->lock);
81 
82 	return ctx;
83 }
84 
85 void __init samsung_clk_of_add_provider(struct device_node *np,
86 				struct samsung_clk_provider *ctx)
87 {
88 	if (np) {
89 		if (of_clk_add_provider(np, of_clk_src_onecell_get,
90 					&ctx->clk_data))
91 			panic("could not register clk provider\n");
92 	}
93 }
94 
95 /* add a clock instance to the clock lookup table used for dt based lookup */
96 void samsung_clk_add_lookup(struct samsung_clk_provider *ctx, struct clk *clk,
97 				unsigned int id)
98 {
99 	if (ctx->clk_data.clks && id)
100 		ctx->clk_data.clks[id] = clk;
101 }
102 
103 /* register a list of aliases */
104 void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx,
105 				const struct samsung_clock_alias *list,
106 				unsigned int nr_clk)
107 {
108 	struct clk *clk;
109 	unsigned int idx, ret;
110 
111 	if (!ctx->clk_data.clks) {
112 		pr_err("%s: clock table missing\n", __func__);
113 		return;
114 	}
115 
116 	for (idx = 0; idx < nr_clk; idx++, list++) {
117 		if (!list->id) {
118 			pr_err("%s: clock id missing for index %d\n", __func__,
119 				idx);
120 			continue;
121 		}
122 
123 		clk = ctx->clk_data.clks[list->id];
124 		if (!clk) {
125 			pr_err("%s: failed to find clock %d\n", __func__,
126 				list->id);
127 			continue;
128 		}
129 
130 		ret = clk_register_clkdev(clk, list->alias, list->dev_name);
131 		if (ret)
132 			pr_err("%s: failed to register lookup %s\n",
133 					__func__, list->alias);
134 	}
135 }
136 
137 /* register a list of fixed clocks */
138 void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx,
139 		const struct samsung_fixed_rate_clock *list,
140 		unsigned int nr_clk)
141 {
142 	struct clk *clk;
143 	unsigned int idx, ret;
144 
145 	for (idx = 0; idx < nr_clk; idx++, list++) {
146 		clk = clk_register_fixed_rate(NULL, list->name,
147 			list->parent_name, list->flags, list->fixed_rate);
148 		if (IS_ERR(clk)) {
149 			pr_err("%s: failed to register clock %s\n", __func__,
150 				list->name);
151 			continue;
152 		}
153 
154 		samsung_clk_add_lookup(ctx, clk, list->id);
155 
156 		/*
157 		 * Unconditionally add a clock lookup for the fixed rate clocks.
158 		 * There are not many of these on any of Samsung platforms.
159 		 */
160 		ret = clk_register_clkdev(clk, list->name, NULL);
161 		if (ret)
162 			pr_err("%s: failed to register clock lookup for %s",
163 				__func__, list->name);
164 	}
165 }
166 
167 /* register a list of fixed factor clocks */
168 void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx,
169 		const struct samsung_fixed_factor_clock *list, unsigned int nr_clk)
170 {
171 	struct clk *clk;
172 	unsigned int idx;
173 
174 	for (idx = 0; idx < nr_clk; idx++, list++) {
175 		clk = clk_register_fixed_factor(NULL, list->name,
176 			list->parent_name, list->flags, list->mult, list->div);
177 		if (IS_ERR(clk)) {
178 			pr_err("%s: failed to register clock %s\n", __func__,
179 				list->name);
180 			continue;
181 		}
182 
183 		samsung_clk_add_lookup(ctx, clk, list->id);
184 	}
185 }
186 
187 /* register a list of mux clocks */
188 void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx,
189 				const struct samsung_mux_clock *list,
190 				unsigned int nr_clk)
191 {
192 	struct clk *clk;
193 	unsigned int idx, ret;
194 
195 	for (idx = 0; idx < nr_clk; idx++, list++) {
196 		clk = clk_register_mux(NULL, list->name, list->parent_names,
197 			list->num_parents, list->flags,
198 			ctx->reg_base + list->offset,
199 			list->shift, list->width, list->mux_flags, &ctx->lock);
200 		if (IS_ERR(clk)) {
201 			pr_err("%s: failed to register clock %s\n", __func__,
202 				list->name);
203 			continue;
204 		}
205 
206 		samsung_clk_add_lookup(ctx, clk, list->id);
207 
208 		/* register a clock lookup only if a clock alias is specified */
209 		if (list->alias) {
210 			ret = clk_register_clkdev(clk, list->alias,
211 						list->dev_name);
212 			if (ret)
213 				pr_err("%s: failed to register lookup %s\n",
214 						__func__, list->alias);
215 		}
216 	}
217 }
218 
219 /* register a list of div clocks */
220 void __init samsung_clk_register_div(struct samsung_clk_provider *ctx,
221 				const struct samsung_div_clock *list,
222 				unsigned int nr_clk)
223 {
224 	struct clk *clk;
225 	unsigned int idx, ret;
226 
227 	for (idx = 0; idx < nr_clk; idx++, list++) {
228 		if (list->table)
229 			clk = clk_register_divider_table(NULL, list->name,
230 				list->parent_name, list->flags,
231 				ctx->reg_base + list->offset,
232 				list->shift, list->width, list->div_flags,
233 				list->table, &ctx->lock);
234 		else
235 			clk = clk_register_divider(NULL, list->name,
236 				list->parent_name, list->flags,
237 				ctx->reg_base + list->offset, list->shift,
238 				list->width, list->div_flags, &ctx->lock);
239 		if (IS_ERR(clk)) {
240 			pr_err("%s: failed to register clock %s\n", __func__,
241 				list->name);
242 			continue;
243 		}
244 
245 		samsung_clk_add_lookup(ctx, clk, list->id);
246 
247 		/* register a clock lookup only if a clock alias is specified */
248 		if (list->alias) {
249 			ret = clk_register_clkdev(clk, list->alias,
250 						list->dev_name);
251 			if (ret)
252 				pr_err("%s: failed to register lookup %s\n",
253 						__func__, list->alias);
254 		}
255 	}
256 }
257 
258 /* register a list of gate clocks */
259 void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx,
260 				const struct samsung_gate_clock *list,
261 				unsigned int nr_clk)
262 {
263 	struct clk *clk;
264 	unsigned int idx, ret;
265 
266 	for (idx = 0; idx < nr_clk; idx++, list++) {
267 		clk = clk_register_gate(NULL, list->name, list->parent_name,
268 				list->flags, ctx->reg_base + list->offset,
269 				list->bit_idx, list->gate_flags, &ctx->lock);
270 		if (IS_ERR(clk)) {
271 			pr_err("%s: failed to register clock %s\n", __func__,
272 				list->name);
273 			continue;
274 		}
275 
276 		/* register a clock lookup only if a clock alias is specified */
277 		if (list->alias) {
278 			ret = clk_register_clkdev(clk, list->alias,
279 							list->dev_name);
280 			if (ret)
281 				pr_err("%s: failed to register lookup %s\n",
282 					__func__, list->alias);
283 		}
284 
285 		samsung_clk_add_lookup(ctx, clk, list->id);
286 	}
287 }
288 
289 /*
290  * obtain the clock speed of all external fixed clock sources from device
291  * tree and register it
292  */
293 void __init samsung_clk_of_register_fixed_ext(struct samsung_clk_provider *ctx,
294 			struct samsung_fixed_rate_clock *fixed_rate_clk,
295 			unsigned int nr_fixed_rate_clk,
296 			const struct of_device_id *clk_matches)
297 {
298 	const struct of_device_id *match;
299 	struct device_node *clk_np;
300 	u32 freq;
301 
302 	for_each_matching_node_and_match(clk_np, clk_matches, &match) {
303 		if (of_property_read_u32(clk_np, "clock-frequency", &freq))
304 			continue;
305 		fixed_rate_clk[(unsigned long)match->data].fixed_rate = freq;
306 	}
307 	samsung_clk_register_fixed_rate(ctx, fixed_rate_clk, nr_fixed_rate_clk);
308 }
309 
310 /* utility function to get the rate of a specified clock */
311 unsigned long _get_rate(const char *clk_name)
312 {
313 	struct clk *clk;
314 
315 	clk = __clk_lookup(clk_name);
316 	if (!clk) {
317 		pr_err("%s: could not find clock %s\n", __func__, clk_name);
318 		return 0;
319 	}
320 
321 	return clk_get_rate(clk);
322 }
323 
324 #ifdef CONFIG_PM_SLEEP
325 static int samsung_clk_suspend(void)
326 {
327 	struct samsung_clock_reg_cache *reg_cache;
328 
329 	list_for_each_entry(reg_cache, &clock_reg_cache_list, node)
330 		samsung_clk_save(reg_cache->reg_base, reg_cache->rdump,
331 				reg_cache->rd_num);
332 	return 0;
333 }
334 
335 static void samsung_clk_resume(void)
336 {
337 	struct samsung_clock_reg_cache *reg_cache;
338 
339 	list_for_each_entry(reg_cache, &clock_reg_cache_list, node)
340 		samsung_clk_restore(reg_cache->reg_base, reg_cache->rdump,
341 				reg_cache->rd_num);
342 }
343 
344 static struct syscore_ops samsung_clk_syscore_ops = {
345 	.suspend = samsung_clk_suspend,
346 	.resume = samsung_clk_resume,
347 };
348 
349 static void samsung_clk_sleep_init(void __iomem *reg_base,
350 		const unsigned long *rdump,
351 		unsigned long nr_rdump)
352 {
353 	struct samsung_clock_reg_cache *reg_cache;
354 
355 	reg_cache = kzalloc(sizeof(struct samsung_clock_reg_cache),
356 			GFP_KERNEL);
357 	if (!reg_cache)
358 		panic("could not allocate register reg_cache.\n");
359 	reg_cache->rdump = samsung_clk_alloc_reg_dump(rdump, nr_rdump);
360 
361 	if (!reg_cache->rdump)
362 		panic("could not allocate register dump storage.\n");
363 
364 	if (list_empty(&clock_reg_cache_list))
365 		register_syscore_ops(&samsung_clk_syscore_ops);
366 
367 	reg_cache->reg_base = reg_base;
368 	reg_cache->rd_num = nr_rdump;
369 	list_add_tail(&reg_cache->node, &clock_reg_cache_list);
370 }
371 
372 #else
373 static void samsung_clk_sleep_init(void __iomem *reg_base,
374 		const unsigned long *rdump,
375 		unsigned long nr_rdump) {}
376 #endif
377 
378 /*
379  * Common function which registers plls, muxes, dividers and gates
380  * for each CMU. It also add CMU register list to register cache.
381  */
382 struct samsung_clk_provider * __init samsung_cmu_register_one(
383 			struct device_node *np,
384 			struct samsung_cmu_info *cmu)
385 {
386 	void __iomem *reg_base;
387 	struct samsung_clk_provider *ctx;
388 
389 	reg_base = of_iomap(np, 0);
390 	if (!reg_base) {
391 		panic("%s: failed to map registers\n", __func__);
392 		return NULL;
393 	}
394 
395 	ctx = samsung_clk_init(np, reg_base, cmu->nr_clk_ids);
396 	if (!ctx) {
397 		panic("%s: unable to allocate ctx\n", __func__);
398 		return ctx;
399 	}
400 
401 	if (cmu->pll_clks)
402 		samsung_clk_register_pll(ctx, cmu->pll_clks, cmu->nr_pll_clks,
403 			reg_base);
404 	if (cmu->mux_clks)
405 		samsung_clk_register_mux(ctx, cmu->mux_clks,
406 			cmu->nr_mux_clks);
407 	if (cmu->div_clks)
408 		samsung_clk_register_div(ctx, cmu->div_clks, cmu->nr_div_clks);
409 	if (cmu->gate_clks)
410 		samsung_clk_register_gate(ctx, cmu->gate_clks,
411 			cmu->nr_gate_clks);
412 	if (cmu->fixed_clks)
413 		samsung_clk_register_fixed_rate(ctx, cmu->fixed_clks,
414 			cmu->nr_fixed_clks);
415 	if (cmu->fixed_factor_clks)
416 		samsung_clk_register_fixed_factor(ctx, cmu->fixed_factor_clks,
417 			cmu->nr_fixed_factor_clks);
418 	if (cmu->clk_regs)
419 		samsung_clk_sleep_init(reg_base, cmu->clk_regs,
420 			cmu->nr_clk_regs);
421 
422 	samsung_clk_of_add_provider(np, ctx);
423 
424 	return ctx;
425 }
426