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/syscore_ops.h>
17 static DEFINE_SPINLOCK(lock
);
18 static struct clk
**clk_table
;
19 static void __iomem
*reg_base
;
21 static struct clk_onecell_data clk_data
;
24 #ifdef CONFIG_PM_SLEEP
25 static struct samsung_clk_reg_dump
*reg_dump
;
26 static unsigned long nr_reg_dump
;
28 static int samsung_clk_suspend(void)
30 struct samsung_clk_reg_dump
*rd
= reg_dump
;
33 for (i
= 0; i
< nr_reg_dump
; i
++, rd
++)
34 rd
->value
= __raw_readl(reg_base
+ rd
->offset
);
39 static void samsung_clk_resume(void)
41 struct samsung_clk_reg_dump
*rd
= reg_dump
;
44 for (i
= 0; i
< nr_reg_dump
; i
++, rd
++)
45 __raw_writel(rd
->value
, reg_base
+ rd
->offset
);
48 static struct syscore_ops samsung_clk_syscore_ops
= {
49 .suspend
= samsung_clk_suspend
,
50 .resume
= samsung_clk_resume
,
52 #endif /* CONFIG_PM_SLEEP */
54 /* setup the essentials required to support clock lookup using ccf */
55 void __init
samsung_clk_init(struct device_node
*np
, void __iomem
*base
,
56 unsigned long nr_clks
, unsigned long *rdump
,
57 unsigned long nr_rdump
, unsigned long *soc_rdump
,
58 unsigned long nr_soc_rdump
)
62 #ifdef CONFIG_PM_SLEEP
63 if (rdump
&& nr_rdump
) {
65 reg_dump
= kzalloc(sizeof(struct samsung_clk_reg_dump
)
66 * (nr_rdump
+ nr_soc_rdump
), GFP_KERNEL
);
68 pr_err("%s: memory alloc for register dump failed\n",
73 for (idx
= 0; idx
< nr_rdump
; idx
++)
74 reg_dump
[idx
].offset
= rdump
[idx
];
75 for (idx
= 0; idx
< nr_soc_rdump
; idx
++)
76 reg_dump
[nr_rdump
+ idx
].offset
= soc_rdump
[idx
];
77 nr_reg_dump
= nr_rdump
+ nr_soc_rdump
;
78 register_syscore_ops(&samsung_clk_syscore_ops
);
82 clk_table
= kzalloc(sizeof(struct clk
*) * nr_clks
, GFP_KERNEL
);
84 panic("could not allocate clock lookup table\n");
90 clk_data
.clks
= clk_table
;
91 clk_data
.clk_num
= nr_clks
;
92 of_clk_add_provider(np
, of_clk_src_onecell_get
, &clk_data
);
96 /* add a clock instance to the clock lookup table used for dt based lookup */
97 void samsung_clk_add_lookup(struct clk
*clk
, unsigned int id
)
103 /* register a list of aliases */
104 void __init
samsung_clk_register_alias(struct samsung_clock_alias
*list
,
108 unsigned int idx
, ret
;
111 pr_err("%s: clock table missing\n", __func__
);
115 for (idx
= 0; idx
< nr_clk
; idx
++, list
++) {
117 pr_err("%s: clock id missing for index %d\n", __func__
,
122 clk
= clk_table
[list
->id
];
124 pr_err("%s: failed to find clock %d\n", __func__
,
129 ret
= clk_register_clkdev(clk
, list
->alias
, list
->dev_name
);
131 pr_err("%s: failed to register lookup %s\n",
132 __func__
, list
->alias
);
136 /* register a list of fixed clocks */
137 void __init
samsung_clk_register_fixed_rate(
138 struct samsung_fixed_rate_clock
*list
, unsigned int nr_clk
)
141 unsigned int idx
, ret
;
143 for (idx
= 0; idx
< nr_clk
; idx
++, list
++) {
144 clk
= clk_register_fixed_rate(NULL
, list
->name
,
145 list
->parent_name
, list
->flags
, list
->fixed_rate
);
147 pr_err("%s: failed to register clock %s\n", __func__
,
152 samsung_clk_add_lookup(clk
, list
->id
);
155 * Unconditionally add a clock lookup for the fixed rate clocks.
156 * There are not many of these on any of Samsung platforms.
158 ret
= clk_register_clkdev(clk
, list
->name
, NULL
);
160 pr_err("%s: failed to register clock lookup for %s",
161 __func__
, list
->name
);
165 /* register a list of fixed factor clocks */
166 void __init
samsung_clk_register_fixed_factor(
167 struct samsung_fixed_factor_clock
*list
, unsigned int nr_clk
)
172 for (idx
= 0; idx
< nr_clk
; idx
++, list
++) {
173 clk
= clk_register_fixed_factor(NULL
, list
->name
,
174 list
->parent_name
, list
->flags
, list
->mult
, list
->div
);
176 pr_err("%s: failed to register clock %s\n", __func__
,
181 samsung_clk_add_lookup(clk
, list
->id
);
185 /* register a list of mux clocks */
186 void __init
samsung_clk_register_mux(struct samsung_mux_clock
*list
,
190 unsigned int idx
, ret
;
192 for (idx
= 0; idx
< nr_clk
; idx
++, list
++) {
193 clk
= clk_register_mux(NULL
, list
->name
, list
->parent_names
,
194 list
->num_parents
, list
->flags
, reg_base
+ list
->offset
,
195 list
->shift
, list
->width
, list
->mux_flags
, &lock
);
197 pr_err("%s: failed to register clock %s\n", __func__
,
202 samsung_clk_add_lookup(clk
, list
->id
);
204 /* register a clock lookup only if a clock alias is specified */
206 ret
= clk_register_clkdev(clk
, list
->alias
,
209 pr_err("%s: failed to register lookup %s\n",
210 __func__
, list
->alias
);
215 /* register a list of div clocks */
216 void __init
samsung_clk_register_div(struct samsung_div_clock
*list
,
220 unsigned int idx
, ret
;
222 for (idx
= 0; idx
< nr_clk
; idx
++, list
++) {
224 clk
= clk_register_divider_table(NULL
, list
->name
,
225 list
->parent_name
, list
->flags
,
226 reg_base
+ list
->offset
, list
->shift
,
227 list
->width
, list
->div_flags
,
230 clk
= clk_register_divider(NULL
, list
->name
,
231 list
->parent_name
, list
->flags
,
232 reg_base
+ list
->offset
, list
->shift
,
233 list
->width
, list
->div_flags
, &lock
);
235 pr_err("%s: failed to register clock %s\n", __func__
,
240 samsung_clk_add_lookup(clk
, list
->id
);
242 /* register a clock lookup only if a clock alias is specified */
244 ret
= clk_register_clkdev(clk
, list
->alias
,
247 pr_err("%s: failed to register lookup %s\n",
248 __func__
, list
->alias
);
253 /* register a list of gate clocks */
254 void __init
samsung_clk_register_gate(struct samsung_gate_clock
*list
,
258 unsigned int idx
, ret
;
260 for (idx
= 0; idx
< nr_clk
; idx
++, list
++) {
261 clk
= clk_register_gate(NULL
, list
->name
, list
->parent_name
,
262 list
->flags
, reg_base
+ list
->offset
,
263 list
->bit_idx
, list
->gate_flags
, &lock
);
265 pr_err("%s: failed to register clock %s\n", __func__
,
270 /* register a clock lookup only if a clock alias is specified */
272 ret
= clk_register_clkdev(clk
, list
->alias
,
275 pr_err("%s: failed to register lookup %s\n",
276 __func__
, list
->alias
);
279 samsung_clk_add_lookup(clk
, list
->id
);
284 * obtain the clock speed of all external fixed clock sources from device
285 * tree and register it
288 void __init
samsung_clk_of_register_fixed_ext(
289 struct samsung_fixed_rate_clock
*fixed_rate_clk
,
290 unsigned int nr_fixed_rate_clk
,
291 struct of_device_id
*clk_matches
)
293 const struct of_device_id
*match
;
294 struct device_node
*np
;
297 for_each_matching_node_and_match(np
, clk_matches
, &match
) {
298 if (of_property_read_u32(np
, "clock-frequency", &freq
))
300 fixed_rate_clk
[(u32
)match
->data
].fixed_rate
= freq
;
302 samsung_clk_register_fixed_rate(fixed_rate_clk
, nr_fixed_rate_clk
);
306 /* utility function to get the rate of a specified clock */
307 unsigned long _get_rate(const char *clk_name
)
311 clk
= __clk_lookup(clk_name
);
313 pr_err("%s: could not find clock %s\n", __func__
, clk_name
);
317 return clk_get_rate(clk
);