2 * Copyright (c) 2013 Samsung Electronics Co., Ltd.
3 * Copyright (c) 2013 Linaro Ltd.
4 * Author: Thomas Abraham <thomas.ab@samsung.com>
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.
10 * This file includes utility functions to register clocks to common
11 * clock framework for Samsung platforms.
14 #include <linux/of_address.h>
15 #include <linux/syscore_ops.h>
19 static LIST_HEAD(clock_reg_cache_list);
21 static struct samsung_clk_suspend_ops *suspend_ops;
23 int samsung_clk_register_suspend_ops(
24 struct samsung_clk_suspend_ops *ops)
30 static int samsung_clk_suspend_prepare(void)
32 if (suspend_ops && suspend_ops->suspend_prepare)
33 return suspend_ops->suspend_prepare();
37 static int samsung_clk_suspend_unprepare(void)
39 if (suspend_ops && suspend_ops->suspend_unprepare)
40 return suspend_ops->suspend_unprepare();
44 void samsung_clk_save(void __iomem *base,
45 struct samsung_clk_reg_dump *rd,
46 unsigned int num_regs)
48 for (; num_regs > 0; --num_regs, ++rd)
49 rd->value = readl(base + rd->offset);
52 void samsung_clk_restore(void __iomem *base,
53 const struct samsung_clk_reg_dump *rd,
54 unsigned int num_regs)
56 for (; num_regs > 0; --num_regs, ++rd)
57 writel(rd->value, base + rd->offset);
60 struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump(
61 const unsigned long *rdump,
62 unsigned long nr_rdump)
64 struct samsung_clk_reg_dump *rd;
67 rd = kcalloc(nr_rdump, sizeof(*rd), GFP_KERNEL);
71 for (i = 0; i < nr_rdump; ++i)
72 rd[i].offset = rdump[i];
77 /* setup the essentials required to support clock lookup using ccf */
78 struct samsung_clk_provider *__init samsung_clk_init(struct device_node *np,
79 void __iomem *base, unsigned long nr_clks)
81 struct samsung_clk_provider *ctx;
82 struct clk **clk_table;
85 ctx = kzalloc(sizeof(struct samsung_clk_provider), GFP_KERNEL);
87 panic("could not allocate clock provider context.\n");
89 clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL);
91 panic("could not allocate clock lookup table\n");
93 for (i = 0; i < nr_clks; ++i)
94 clk_table[i] = ERR_PTR(-ENOENT);
97 ctx->clk_data.clks = clk_table;
98 ctx->clk_data.clk_num = nr_clks;
99 spin_lock_init(&ctx->lock);
104 void __init samsung_clk_of_add_provider(struct device_node *np,
105 struct samsung_clk_provider *ctx)
108 if (of_clk_add_provider(np, of_clk_src_onecell_get,
110 panic("could not register clk provider\n");
114 /* add a clock instance to the clock lookup table used for dt based lookup */
115 void samsung_clk_add_lookup(struct samsung_clk_provider *ctx, struct clk *clk,
118 if (ctx->clk_data.clks && id)
119 ctx->clk_data.clks[id] = clk;
122 /* register a list of aliases */
123 void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx,
124 struct samsung_clock_alias *list,
128 unsigned int idx, ret;
130 if (!ctx->clk_data.clks) {
131 pr_err("%s: clock table missing\n", __func__);
135 for (idx = 0; idx < nr_clk; idx++, list++) {
137 pr_err("%s: clock id missing for index %d\n", __func__,
142 clk = ctx->clk_data.clks[list->id];
144 pr_err("%s: failed to find clock %d\n", __func__,
149 ret = clk_register_clkdev(clk, list->alias, list->dev_name);
151 pr_err("%s: failed to register lookup %s\n",
152 __func__, list->alias);
156 /* register a list of fixed clocks */
157 void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx,
158 struct samsung_fixed_rate_clock *list, unsigned int nr_clk)
161 unsigned int idx, ret;
163 for (idx = 0; idx < nr_clk; idx++, list++) {
164 clk = clk_register_fixed_rate(NULL, list->name,
165 list->parent_name, list->flags, list->fixed_rate);
167 pr_err("%s: failed to register clock %s\n", __func__,
172 samsung_clk_add_lookup(ctx, clk, list->id);
175 * Unconditionally add a clock lookup for the fixed rate clocks.
176 * There are not many of these on any of Samsung platforms.
178 ret = clk_register_clkdev(clk, list->name, NULL);
180 pr_err("%s: failed to register clock lookup for %s",
181 __func__, list->name);
185 /* register a list of fixed factor clocks */
186 void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx,
187 struct samsung_fixed_factor_clock *list, unsigned int nr_clk)
192 for (idx = 0; idx < nr_clk; idx++, list++) {
193 clk = clk_register_fixed_factor(NULL, list->name,
194 list->parent_name, list->flags, list->mult, list->div);
196 pr_err("%s: failed to register clock %s\n", __func__,
201 samsung_clk_add_lookup(ctx, clk, list->id);
205 /* register a list of mux clocks */
206 void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx,
207 struct samsung_mux_clock *list,
211 unsigned int idx, ret;
213 for (idx = 0; idx < nr_clk; idx++, list++) {
214 clk = clk_register_mux(NULL, list->name, list->parent_names,
215 list->num_parents, list->flags,
216 ctx->reg_base + list->offset,
217 list->shift, list->width, list->mux_flags, &ctx->lock);
219 pr_err("%s: failed to register clock %s\n", __func__,
224 samsung_clk_add_lookup(ctx, clk, list->id);
226 /* register a clock lookup only if a clock alias is specified */
228 ret = clk_register_clkdev(clk, list->alias,
231 pr_err("%s: failed to register lookup %s\n",
232 __func__, list->alias);
237 /* register a list of div clocks */
238 void __init samsung_clk_register_div(struct samsung_clk_provider *ctx,
239 struct samsung_div_clock *list,
243 unsigned int idx, ret;
245 for (idx = 0; idx < nr_clk; idx++, list++) {
247 clk = clk_register_divider_table(NULL, list->name,
248 list->parent_name, list->flags,
249 ctx->reg_base + list->offset,
250 list->shift, list->width, list->div_flags,
251 list->table, &ctx->lock);
253 clk = clk_register_divider(NULL, list->name,
254 list->parent_name, list->flags,
255 ctx->reg_base + list->offset, list->shift,
256 list->width, list->div_flags, &ctx->lock);
258 pr_err("%s: failed to register clock %s\n", __func__,
263 samsung_clk_add_lookup(ctx, clk, list->id);
265 /* register a clock lookup only if a clock alias is specified */
267 ret = clk_register_clkdev(clk, list->alias,
270 pr_err("%s: failed to register lookup %s\n",
271 __func__, list->alias);
276 /* register a list of gate clocks */
277 void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx,
278 struct samsung_gate_clock *list,
282 unsigned int idx, ret;
284 for (idx = 0; idx < nr_clk; idx++, list++) {
285 clk = clk_register_gate(NULL, list->name, list->parent_name,
286 list->flags, ctx->reg_base + list->offset,
287 list->bit_idx, list->gate_flags, &ctx->lock);
289 pr_err("%s: failed to register clock %s\n", __func__,
294 /* register a clock lookup only if a clock alias is specified */
296 ret = clk_register_clkdev(clk, list->alias,
299 pr_err("%s: failed to register lookup %s\n",
300 __func__, list->alias);
303 samsung_clk_add_lookup(ctx, clk, list->id);
308 * obtain the clock speed of all external fixed clock sources from device
309 * tree and register it
311 void __init samsung_clk_of_register_fixed_ext(struct samsung_clk_provider *ctx,
312 struct samsung_fixed_rate_clock *fixed_rate_clk,
313 unsigned int nr_fixed_rate_clk,
314 const struct of_device_id *clk_matches)
316 const struct of_device_id *match;
317 struct device_node *clk_np;
320 for_each_matching_node_and_match(clk_np, clk_matches, &match) {
321 if (of_property_read_u32(clk_np, "clock-frequency", &freq))
323 fixed_rate_clk[(unsigned long)match->data].fixed_rate = freq;
325 samsung_clk_register_fixed_rate(ctx, fixed_rate_clk, nr_fixed_rate_clk);
328 /* utility function to get the rate of a specified clock */
329 unsigned long _get_rate(const char *clk_name)
333 clk = __clk_lookup(clk_name);
335 pr_err("%s: could not find clock %s\n", __func__, clk_name);
339 return clk_get_rate(clk);
342 #ifdef CONFIG_PM_SLEEP
343 static int samsung_clk_suspend(void)
345 struct samsung_clock_reg_cache *reg_cache;
348 ret = samsung_clk_suspend_prepare();
352 list_for_each_entry(reg_cache, &clock_reg_cache_list, node)
353 samsung_clk_save(reg_cache->reg_base, reg_cache->rdump,
358 static void samsung_clk_resume(void)
360 struct samsung_clock_reg_cache *reg_cache;
362 list_for_each_entry(reg_cache, &clock_reg_cache_list, node)
363 samsung_clk_restore(reg_cache->reg_base, reg_cache->rdump,
366 samsung_clk_suspend_unprepare();
369 static struct syscore_ops samsung_clk_syscore_ops = {
370 .suspend = samsung_clk_suspend,
371 .resume = samsung_clk_resume,
374 static void samsung_clk_sleep_init(void __iomem *reg_base,
375 const unsigned long *rdump,
376 unsigned long nr_rdump)
378 struct samsung_clock_reg_cache *reg_cache;
380 reg_cache = kzalloc(sizeof(struct samsung_clock_reg_cache),
383 panic("could not allocate register reg_cache.\n");
384 reg_cache->rdump = samsung_clk_alloc_reg_dump(rdump, nr_rdump);
386 if (!reg_cache->rdump)
387 panic("could not allocate register dump storage.\n");
389 if (list_empty(&clock_reg_cache_list))
390 register_syscore_ops(&samsung_clk_syscore_ops);
392 reg_cache->reg_base = reg_base;
393 reg_cache->rd_num = nr_rdump;
394 list_add_tail(®_cache->node, &clock_reg_cache_list);
398 static void samsung_clk_sleep_init(void __iomem *reg_base,
399 const unsigned long *rdump,
400 unsigned long nr_rdump) {}
404 * Common function which registers plls, muxes, dividers and gates
405 * for each CMU. It also add CMU register list to register cache.
407 struct samsung_clk_provider * __init samsung_cmu_register_one(
408 struct device_node *np,
409 struct samsung_cmu_info *cmu)
411 void __iomem *reg_base;
412 struct samsung_clk_provider *ctx;
414 reg_base = of_iomap(np, 0);
416 panic("%s: failed to map registers\n", __func__);
420 ctx = samsung_clk_init(np, reg_base, cmu->nr_clk_ids);
422 panic("%s: unable to alllocate ctx\n", __func__);
427 samsung_clk_register_pll(ctx, cmu->pll_clks, cmu->nr_pll_clks,
430 samsung_clk_register_mux(ctx, cmu->mux_clks,
433 samsung_clk_register_div(ctx, cmu->div_clks, cmu->nr_div_clks);
435 samsung_clk_register_gate(ctx, cmu->gate_clks,
438 samsung_clk_register_fixed_rate(ctx, cmu->fixed_clks,
440 if (cmu->fixed_factor_clks)
441 samsung_clk_register_fixed_factor(ctx, cmu->fixed_factor_clks,
442 cmu->nr_fixed_factor_clks);
444 samsung_clk_sleep_init(reg_base, cmu->clk_regs,
447 samsung_clk_of_add_provider(np, ctx);