1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2013 Samsung Electronics Co., Ltd.
4 * Copyright (c) 2013 Linaro Ltd.
5 * Author: Thomas Abraham <thomas.ab@samsung.com>
7 * This file includes utility functions to register clocks to common
8 * clock framework for Samsung platforms.
11 #include <linux/slab.h>
12 #include <linux/clkdev.h>
13 #include <linux/clk.h>
14 #include <linux/clk-provider.h>
16 #include <linux/of_address.h>
17 #include <linux/syscore_ops.h>
21 static LIST_HEAD(clock_reg_cache_list
);
23 void samsung_clk_save(void __iomem
*base
,
24 struct samsung_clk_reg_dump
*rd
,
25 unsigned int num_regs
)
27 for (; num_regs
> 0; --num_regs
, ++rd
)
28 rd
->value
= readl(base
+ rd
->offset
);
31 void samsung_clk_restore(void __iomem
*base
,
32 const struct samsung_clk_reg_dump
*rd
,
33 unsigned int num_regs
)
35 for (; num_regs
> 0; --num_regs
, ++rd
)
36 writel(rd
->value
, base
+ rd
->offset
);
39 struct samsung_clk_reg_dump
*samsung_clk_alloc_reg_dump(
40 const unsigned long *rdump
,
41 unsigned long nr_rdump
)
43 struct samsung_clk_reg_dump
*rd
;
46 rd
= kcalloc(nr_rdump
, sizeof(*rd
), GFP_KERNEL
);
50 for (i
= 0; i
< nr_rdump
; ++i
)
51 rd
[i
].offset
= rdump
[i
];
56 /* setup the essentials required to support clock lookup using ccf */
57 struct samsung_clk_provider
*__init
samsung_clk_init(struct device_node
*np
,
58 void __iomem
*base
, unsigned long nr_clks
)
60 struct samsung_clk_provider
*ctx
;
63 ctx
= kzalloc(struct_size(ctx
, clk_data
.hws
, nr_clks
), GFP_KERNEL
);
65 panic("could not allocate clock provider context.\n");
67 for (i
= 0; i
< nr_clks
; ++i
)
68 ctx
->clk_data
.hws
[i
] = ERR_PTR(-ENOENT
);
71 ctx
->clk_data
.num
= nr_clks
;
72 spin_lock_init(&ctx
->lock
);
77 void __init
samsung_clk_of_add_provider(struct device_node
*np
,
78 struct samsung_clk_provider
*ctx
)
81 if (of_clk_add_hw_provider(np
, of_clk_hw_onecell_get
,
83 panic("could not register clk provider\n");
87 /* add a clock instance to the clock lookup table used for dt based lookup */
88 void samsung_clk_add_lookup(struct samsung_clk_provider
*ctx
,
89 struct clk_hw
*clk_hw
, unsigned int id
)
92 ctx
->clk_data
.hws
[id
] = clk_hw
;
95 /* register a list of aliases */
96 void __init
samsung_clk_register_alias(struct samsung_clk_provider
*ctx
,
97 const struct samsung_clock_alias
*list
,
100 struct clk_hw
*clk_hw
;
101 unsigned int idx
, ret
;
103 for (idx
= 0; idx
< nr_clk
; idx
++, list
++) {
105 pr_err("%s: clock id missing for index %d\n", __func__
,
110 clk_hw
= ctx
->clk_data
.hws
[list
->id
];
112 pr_err("%s: failed to find clock %d\n", __func__
,
117 ret
= clk_hw_register_clkdev(clk_hw
, list
->alias
,
120 pr_err("%s: failed to register lookup %s\n",
121 __func__
, list
->alias
);
125 /* register a list of fixed clocks */
126 void __init
samsung_clk_register_fixed_rate(struct samsung_clk_provider
*ctx
,
127 const struct samsung_fixed_rate_clock
*list
,
130 struct clk_hw
*clk_hw
;
131 unsigned int idx
, ret
;
133 for (idx
= 0; idx
< nr_clk
; idx
++, list
++) {
134 clk_hw
= clk_hw_register_fixed_rate(ctx
->dev
, list
->name
,
135 list
->parent_name
, list
->flags
, list
->fixed_rate
);
136 if (IS_ERR(clk_hw
)) {
137 pr_err("%s: failed to register clock %s\n", __func__
,
142 samsung_clk_add_lookup(ctx
, clk_hw
, list
->id
);
145 * Unconditionally add a clock lookup for the fixed rate clocks.
146 * There are not many of these on any of Samsung platforms.
148 ret
= clk_hw_register_clkdev(clk_hw
, list
->name
, NULL
);
150 pr_err("%s: failed to register clock lookup for %s",
151 __func__
, list
->name
);
155 /* register a list of fixed factor clocks */
156 void __init
samsung_clk_register_fixed_factor(struct samsung_clk_provider
*ctx
,
157 const struct samsung_fixed_factor_clock
*list
, unsigned int nr_clk
)
159 struct clk_hw
*clk_hw
;
162 for (idx
= 0; idx
< nr_clk
; idx
++, list
++) {
163 clk_hw
= clk_hw_register_fixed_factor(ctx
->dev
, list
->name
,
164 list
->parent_name
, list
->flags
, list
->mult
, list
->div
);
165 if (IS_ERR(clk_hw
)) {
166 pr_err("%s: failed to register clock %s\n", __func__
,
171 samsung_clk_add_lookup(ctx
, clk_hw
, list
->id
);
175 /* register a list of mux clocks */
176 void __init
samsung_clk_register_mux(struct samsung_clk_provider
*ctx
,
177 const struct samsung_mux_clock
*list
,
180 struct clk_hw
*clk_hw
;
183 for (idx
= 0; idx
< nr_clk
; idx
++, list
++) {
184 clk_hw
= clk_hw_register_mux(ctx
->dev
, list
->name
,
185 list
->parent_names
, list
->num_parents
, list
->flags
,
186 ctx
->reg_base
+ list
->offset
,
187 list
->shift
, list
->width
, list
->mux_flags
, &ctx
->lock
);
188 if (IS_ERR(clk_hw
)) {
189 pr_err("%s: failed to register clock %s\n", __func__
,
194 samsung_clk_add_lookup(ctx
, clk_hw
, list
->id
);
198 /* register a list of div clocks */
199 void __init
samsung_clk_register_div(struct samsung_clk_provider
*ctx
,
200 const struct samsung_div_clock
*list
,
203 struct clk_hw
*clk_hw
;
206 for (idx
= 0; idx
< nr_clk
; idx
++, list
++) {
208 clk_hw
= clk_hw_register_divider_table(ctx
->dev
,
209 list
->name
, list
->parent_name
, list
->flags
,
210 ctx
->reg_base
+ list
->offset
,
211 list
->shift
, list
->width
, list
->div_flags
,
212 list
->table
, &ctx
->lock
);
214 clk_hw
= clk_hw_register_divider(ctx
->dev
, list
->name
,
215 list
->parent_name
, list
->flags
,
216 ctx
->reg_base
+ list
->offset
, list
->shift
,
217 list
->width
, list
->div_flags
, &ctx
->lock
);
218 if (IS_ERR(clk_hw
)) {
219 pr_err("%s: failed to register clock %s\n", __func__
,
224 samsung_clk_add_lookup(ctx
, clk_hw
, list
->id
);
228 /* register a list of gate clocks */
229 void __init
samsung_clk_register_gate(struct samsung_clk_provider
*ctx
,
230 const struct samsung_gate_clock
*list
,
233 struct clk_hw
*clk_hw
;
236 for (idx
= 0; idx
< nr_clk
; idx
++, list
++) {
237 clk_hw
= clk_hw_register_gate(ctx
->dev
, list
->name
, list
->parent_name
,
238 list
->flags
, ctx
->reg_base
+ list
->offset
,
239 list
->bit_idx
, list
->gate_flags
, &ctx
->lock
);
240 if (IS_ERR(clk_hw
)) {
241 pr_err("%s: failed to register clock %s\n", __func__
,
246 samsung_clk_add_lookup(ctx
, clk_hw
, list
->id
);
251 * obtain the clock speed of all external fixed clock sources from device
252 * tree and register it
254 void __init
samsung_clk_of_register_fixed_ext(struct samsung_clk_provider
*ctx
,
255 struct samsung_fixed_rate_clock
*fixed_rate_clk
,
256 unsigned int nr_fixed_rate_clk
,
257 const struct of_device_id
*clk_matches
)
259 const struct of_device_id
*match
;
260 struct device_node
*clk_np
;
263 for_each_matching_node_and_match(clk_np
, clk_matches
, &match
) {
264 if (of_property_read_u32(clk_np
, "clock-frequency", &freq
))
266 fixed_rate_clk
[(unsigned long)match
->data
].fixed_rate
= freq
;
268 samsung_clk_register_fixed_rate(ctx
, fixed_rate_clk
, nr_fixed_rate_clk
);
271 /* utility function to get the rate of a specified clock */
272 unsigned long _get_rate(const char *clk_name
)
276 clk
= __clk_lookup(clk_name
);
278 pr_err("%s: could not find clock %s\n", __func__
, clk_name
);
282 return clk_get_rate(clk
);
285 #ifdef CONFIG_PM_SLEEP
286 static int samsung_clk_suspend(void)
288 struct samsung_clock_reg_cache
*reg_cache
;
290 list_for_each_entry(reg_cache
, &clock_reg_cache_list
, node
) {
291 samsung_clk_save(reg_cache
->reg_base
, reg_cache
->rdump
,
293 samsung_clk_restore(reg_cache
->reg_base
, reg_cache
->rsuspend
,
294 reg_cache
->rsuspend_num
);
299 static void samsung_clk_resume(void)
301 struct samsung_clock_reg_cache
*reg_cache
;
303 list_for_each_entry(reg_cache
, &clock_reg_cache_list
, node
)
304 samsung_clk_restore(reg_cache
->reg_base
, reg_cache
->rdump
,
308 static struct syscore_ops samsung_clk_syscore_ops
= {
309 .suspend
= samsung_clk_suspend
,
310 .resume
= samsung_clk_resume
,
313 void samsung_clk_extended_sleep_init(void __iomem
*reg_base
,
314 const unsigned long *rdump
,
315 unsigned long nr_rdump
,
316 const struct samsung_clk_reg_dump
*rsuspend
,
317 unsigned long nr_rsuspend
)
319 struct samsung_clock_reg_cache
*reg_cache
;
321 reg_cache
= kzalloc(sizeof(struct samsung_clock_reg_cache
),
324 panic("could not allocate register reg_cache.\n");
325 reg_cache
->rdump
= samsung_clk_alloc_reg_dump(rdump
, nr_rdump
);
327 if (!reg_cache
->rdump
)
328 panic("could not allocate register dump storage.\n");
330 if (list_empty(&clock_reg_cache_list
))
331 register_syscore_ops(&samsung_clk_syscore_ops
);
333 reg_cache
->reg_base
= reg_base
;
334 reg_cache
->rd_num
= nr_rdump
;
335 reg_cache
->rsuspend
= rsuspend
;
336 reg_cache
->rsuspend_num
= nr_rsuspend
;
337 list_add_tail(®_cache
->node
, &clock_reg_cache_list
);
342 * Common function which registers plls, muxes, dividers and gates
343 * for each CMU. It also add CMU register list to register cache.
345 struct samsung_clk_provider
* __init
samsung_cmu_register_one(
346 struct device_node
*np
,
347 const struct samsung_cmu_info
*cmu
)
349 void __iomem
*reg_base
;
350 struct samsung_clk_provider
*ctx
;
352 reg_base
= of_iomap(np
, 0);
354 panic("%s: failed to map registers\n", __func__
);
358 ctx
= samsung_clk_init(np
, reg_base
, cmu
->nr_clk_ids
);
361 samsung_clk_register_pll(ctx
, cmu
->pll_clks
, cmu
->nr_pll_clks
,
364 samsung_clk_register_mux(ctx
, cmu
->mux_clks
,
367 samsung_clk_register_div(ctx
, cmu
->div_clks
, cmu
->nr_div_clks
);
369 samsung_clk_register_gate(ctx
, cmu
->gate_clks
,
372 samsung_clk_register_fixed_rate(ctx
, cmu
->fixed_clks
,
374 if (cmu
->fixed_factor_clks
)
375 samsung_clk_register_fixed_factor(ctx
, cmu
->fixed_factor_clks
,
376 cmu
->nr_fixed_factor_clks
);
378 samsung_clk_extended_sleep_init(reg_base
,
379 cmu
->clk_regs
, cmu
->nr_clk_regs
,
380 cmu
->suspend_regs
, cmu
->nr_suspend_regs
);
382 samsung_clk_of_add_provider(np
, ctx
);