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/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>
23 static LIST_HEAD(clock_reg_cache_list
);
25 void samsung_clk_save(void __iomem
*base
,
26 struct samsung_clk_reg_dump
*rd
,
27 unsigned int num_regs
)
29 for (; num_regs
> 0; --num_regs
, ++rd
)
30 rd
->value
= readl(base
+ rd
->offset
);
33 void samsung_clk_restore(void __iomem
*base
,
34 const struct samsung_clk_reg_dump
*rd
,
35 unsigned int num_regs
)
37 for (; num_regs
> 0; --num_regs
, ++rd
)
38 writel(rd
->value
, base
+ rd
->offset
);
41 struct samsung_clk_reg_dump
*samsung_clk_alloc_reg_dump(
42 const unsigned long *rdump
,
43 unsigned long nr_rdump
)
45 struct samsung_clk_reg_dump
*rd
;
48 rd
= kcalloc(nr_rdump
, sizeof(*rd
), GFP_KERNEL
);
52 for (i
= 0; i
< nr_rdump
; ++i
)
53 rd
[i
].offset
= rdump
[i
];
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
)
62 struct samsung_clk_provider
*ctx
;
63 struct clk
**clk_table
;
66 ctx
= kzalloc(sizeof(struct samsung_clk_provider
), GFP_KERNEL
);
68 panic("could not allocate clock provider context.\n");
70 clk_table
= kcalloc(nr_clks
, sizeof(struct clk
*), GFP_KERNEL
);
72 panic("could not allocate clock lookup table\n");
74 for (i
= 0; i
< nr_clks
; ++i
)
75 clk_table
[i
] = ERR_PTR(-ENOENT
);
78 ctx
->clk_data
.clks
= clk_table
;
79 ctx
->clk_data
.clk_num
= nr_clks
;
80 spin_lock_init(&ctx
->lock
);
85 void __init
samsung_clk_of_add_provider(struct device_node
*np
,
86 struct samsung_clk_provider
*ctx
)
89 if (of_clk_add_provider(np
, of_clk_src_onecell_get
,
91 panic("could not register clk provider\n");
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
,
99 if (ctx
->clk_data
.clks
&& id
)
100 ctx
->clk_data
.clks
[id
] = clk
;
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
,
109 unsigned int idx
, ret
;
111 if (!ctx
->clk_data
.clks
) {
112 pr_err("%s: clock table missing\n", __func__
);
116 for (idx
= 0; idx
< nr_clk
; idx
++, list
++) {
118 pr_err("%s: clock id missing for index %d\n", __func__
,
123 clk
= ctx
->clk_data
.clks
[list
->id
];
125 pr_err("%s: failed to find clock %d\n", __func__
,
130 ret
= clk_register_clkdev(clk
, list
->alias
, list
->dev_name
);
132 pr_err("%s: failed to register lookup %s\n",
133 __func__
, list
->alias
);
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
,
143 unsigned int idx
, ret
;
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
);
149 pr_err("%s: failed to register clock %s\n", __func__
,
154 samsung_clk_add_lookup(ctx
, clk
, list
->id
);
157 * Unconditionally add a clock lookup for the fixed rate clocks.
158 * There are not many of these on any of Samsung platforms.
160 ret
= clk_register_clkdev(clk
, list
->name
, NULL
);
162 pr_err("%s: failed to register clock lookup for %s",
163 __func__
, list
->name
);
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
)
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
);
178 pr_err("%s: failed to register clock %s\n", __func__
,
183 samsung_clk_add_lookup(ctx
, clk
, list
->id
);
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
,
193 unsigned int idx
, ret
;
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
);
201 pr_err("%s: failed to register clock %s\n", __func__
,
206 samsung_clk_add_lookup(ctx
, clk
, list
->id
);
208 /* register a clock lookup only if a clock alias is specified */
210 ret
= clk_register_clkdev(clk
, list
->alias
,
213 pr_err("%s: failed to register lookup %s\n",
214 __func__
, list
->alias
);
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
,
225 unsigned int idx
, ret
;
227 for (idx
= 0; idx
< nr_clk
; idx
++, list
++) {
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
);
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
);
240 pr_err("%s: failed to register clock %s\n", __func__
,
245 samsung_clk_add_lookup(ctx
, clk
, list
->id
);
247 /* register a clock lookup only if a clock alias is specified */
249 ret
= clk_register_clkdev(clk
, list
->alias
,
252 pr_err("%s: failed to register lookup %s\n",
253 __func__
, list
->alias
);
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
,
264 unsigned int idx
, ret
;
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
);
271 pr_err("%s: failed to register clock %s\n", __func__
,
276 /* register a clock lookup only if a clock alias is specified */
278 ret
= clk_register_clkdev(clk
, list
->alias
,
281 pr_err("%s: failed to register lookup %s\n",
282 __func__
, list
->alias
);
285 samsung_clk_add_lookup(ctx
, clk
, list
->id
);
290 * obtain the clock speed of all external fixed clock sources from device
291 * tree and register it
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
)
298 const struct of_device_id
*match
;
299 struct device_node
*clk_np
;
302 for_each_matching_node_and_match(clk_np
, clk_matches
, &match
) {
303 if (of_property_read_u32(clk_np
, "clock-frequency", &freq
))
305 fixed_rate_clk
[(unsigned long)match
->data
].fixed_rate
= freq
;
307 samsung_clk_register_fixed_rate(ctx
, fixed_rate_clk
, nr_fixed_rate_clk
);
310 /* utility function to get the rate of a specified clock */
311 unsigned long _get_rate(const char *clk_name
)
315 clk
= __clk_lookup(clk_name
);
317 pr_err("%s: could not find clock %s\n", __func__
, clk_name
);
321 return clk_get_rate(clk
);
324 #ifdef CONFIG_PM_SLEEP
325 static int samsung_clk_suspend(void)
327 struct samsung_clock_reg_cache
*reg_cache
;
329 list_for_each_entry(reg_cache
, &clock_reg_cache_list
, node
)
330 samsung_clk_save(reg_cache
->reg_base
, reg_cache
->rdump
,
335 static void samsung_clk_resume(void)
337 struct samsung_clock_reg_cache
*reg_cache
;
339 list_for_each_entry(reg_cache
, &clock_reg_cache_list
, node
)
340 samsung_clk_restore(reg_cache
->reg_base
, reg_cache
->rdump
,
344 static struct syscore_ops samsung_clk_syscore_ops
= {
345 .suspend
= samsung_clk_suspend
,
346 .resume
= samsung_clk_resume
,
349 void samsung_clk_sleep_init(void __iomem
*reg_base
,
350 const unsigned long *rdump
,
351 unsigned long nr_rdump
)
353 struct samsung_clock_reg_cache
*reg_cache
;
355 reg_cache
= kzalloc(sizeof(struct samsung_clock_reg_cache
),
358 panic("could not allocate register reg_cache.\n");
359 reg_cache
->rdump
= samsung_clk_alloc_reg_dump(rdump
, nr_rdump
);
361 if (!reg_cache
->rdump
)
362 panic("could not allocate register dump storage.\n");
364 if (list_empty(&clock_reg_cache_list
))
365 register_syscore_ops(&samsung_clk_syscore_ops
);
367 reg_cache
->reg_base
= reg_base
;
368 reg_cache
->rd_num
= nr_rdump
;
369 list_add_tail(®_cache
->node
, &clock_reg_cache_list
);
373 void samsung_clk_sleep_init(void __iomem
*reg_base
,
374 const unsigned long *rdump
,
375 unsigned long nr_rdump
) {}
379 * Common function which registers plls, muxes, dividers and gates
380 * for each CMU. It also add CMU register list to register cache.
382 struct samsung_clk_provider
* __init
samsung_cmu_register_one(
383 struct device_node
*np
,
384 const struct samsung_cmu_info
*cmu
)
386 void __iomem
*reg_base
;
387 struct samsung_clk_provider
*ctx
;
389 reg_base
= of_iomap(np
, 0);
391 panic("%s: failed to map registers\n", __func__
);
395 ctx
= samsung_clk_init(np
, reg_base
, cmu
->nr_clk_ids
);
397 panic("%s: unable to allocate ctx\n", __func__
);
402 samsung_clk_register_pll(ctx
, cmu
->pll_clks
, cmu
->nr_pll_clks
,
405 samsung_clk_register_mux(ctx
, cmu
->mux_clks
,
408 samsung_clk_register_div(ctx
, cmu
->div_clks
, cmu
->nr_div_clks
);
410 samsung_clk_register_gate(ctx
, cmu
->gate_clks
,
413 samsung_clk_register_fixed_rate(ctx
, cmu
->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
);
419 samsung_clk_sleep_init(reg_base
, cmu
->clk_regs
,
422 samsung_clk_of_add_provider(np
, ctx
);