aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk/samsung/clk.c
diff options
context:
space:
mode:
authorThomas Abraham <thomas.abraham@linaro.org>2013-03-09 03:02:44 -0500
committerKukjin Kim <kgene.kim@samsung.com>2013-03-25 05:16:09 -0400
commit721c42a351b113a9633d2447d9131fe620f2d805 (patch)
tree9e52f223570c56cb9c5e775670bbc2e2f0fd03f8 /drivers/clk/samsung/clk.c
parent8ec46b97f24d60645c8f708c87e0caecebd25d77 (diff)
clk: samsung: add common clock framework helper functions for Samsung platforms
All Samsung platforms include different types of clock including fixed-rate, mux, divider and gate clock types. There are typically hundreds of such clocks on each of the Samsung platforms. To enable Samsung platforms to register these clocks using the common clock framework, a bunch of utility functions are introduced here which simplify the clock registration process. The clocks are usually statically instantiated and registered with common clock framework. Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Tested-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Reviewed-by: Tomasz Figa <t.figa@samsung.com> Tested-by: Tomasz Figa <t.figa@samsung.com> Tested-by: Heiko Stuebner <heiko@sntech.de> Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org> Acked-by: Mike Turquette <mturquette@linaro.org> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
Diffstat (limited to 'drivers/clk/samsung/clk.c')
-rw-r--r--drivers/clk/samsung/clk.c273
1 files changed, 273 insertions, 0 deletions
diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c
new file mode 100644
index 000000000000..91d12f397f5d
--- /dev/null
+++ b/drivers/clk/samsung/clk.c
@@ -0,0 +1,273 @@
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/syscore_ops.h>
15#include "clk.h"
16
17static DEFINE_SPINLOCK(lock);
18static struct clk **clk_table;
19static void __iomem *reg_base;
20#ifdef CONFIG_OF
21static struct clk_onecell_data clk_data;
22#endif
23
24#ifdef CONFIG_PM_SLEEP
25static struct samsung_clk_reg_dump *reg_dump;
26static unsigned long nr_reg_dump;
27
28static int samsung_clk_suspend(void)
29{
30 struct samsung_clk_reg_dump *rd = reg_dump;
31 unsigned long i;
32
33 for (i = 0; i < nr_reg_dump; i++, rd++)
34 rd->value = __raw_readl(reg_base + rd->offset);
35
36 return 0;
37}
38
39static void samsung_clk_resume(void)
40{
41 struct samsung_clk_reg_dump *rd = reg_dump;
42 unsigned long i;
43
44 for (i = 0; i < nr_reg_dump; i++, rd++)
45 __raw_writel(rd->value, reg_base + rd->offset);
46}
47
48static struct syscore_ops samsung_clk_syscore_ops = {
49 .suspend = samsung_clk_suspend,
50 .resume = samsung_clk_resume,
51};
52#endif /* CONFIG_PM_SLEEP */
53
54/* setup the essentials required to support clock lookup using ccf */
55void __init samsung_clk_init(struct device_node *np, void __iomem *base,
56 unsigned long nr_clks, unsigned long *rdump,
57 unsigned long nr_rdump)
58{
59 reg_base = base;
60 if (!np)
61 return;
62
63#ifdef CONFIG_OF
64 clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL);
65 if (!clk_table)
66 panic("could not allocate clock lookup table\n");
67
68 clk_data.clks = clk_table;
69 clk_data.clk_num = nr_clks;
70 of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
71#endif
72
73#ifdef CONFIG_PM_SLEEP
74 if (rdump && nr_rdump) {
75 unsigned int idx;
76 reg_dump = kzalloc(sizeof(struct samsung_clk_reg_dump)
77 * nr_rdump, GFP_KERNEL);
78 if (!reg_dump) {
79 pr_err("%s: memory alloc for register dump failed\n",
80 __func__);
81 return;
82 }
83
84 for (idx = 0; idx < nr_rdump; idx++)
85 reg_dump[idx].offset = rdump[idx];
86 nr_reg_dump = nr_rdump;
87 register_syscore_ops(&samsung_clk_syscore_ops);
88 }
89#endif
90}
91
92/* add a clock instance to the clock lookup table used for dt based lookup */
93void samsung_clk_add_lookup(struct clk *clk, unsigned int id)
94{
95 if (clk_table && id)
96 clk_table[id] = clk;
97}
98
99/* register a list of fixed clocks */
100void __init samsung_clk_register_fixed_rate(
101 struct samsung_fixed_rate_clock *list, unsigned int nr_clk)
102{
103 struct clk *clk;
104 unsigned int idx, ret;
105
106 for (idx = 0; idx < nr_clk; idx++, list++) {
107 clk = clk_register_fixed_rate(NULL, list->name,
108 list->parent_name, list->flags, list->fixed_rate);
109 if (IS_ERR(clk)) {
110 pr_err("%s: failed to register clock %s\n", __func__,
111 list->name);
112 continue;
113 }
114
115 samsung_clk_add_lookup(clk, list->id);
116
117 /*
118 * Unconditionally add a clock lookup for the fixed rate clocks.
119 * There are not many of these on any of Samsung platforms.
120 */
121 ret = clk_register_clkdev(clk, list->name, NULL);
122 if (ret)
123 pr_err("%s: failed to register clock lookup for %s",
124 __func__, list->name);
125 }
126}
127
128/* register a list of fixed factor clocks */
129void __init samsung_clk_register_fixed_factor(
130 struct samsung_fixed_factor_clock *list, unsigned int nr_clk)
131{
132 struct clk *clk;
133 unsigned int idx;
134
135 for (idx = 0; idx < nr_clk; idx++, list++) {
136 clk = clk_register_fixed_factor(NULL, list->name,
137 list->parent_name, list->flags, list->mult, list->div);
138 if (IS_ERR(clk)) {
139 pr_err("%s: failed to register clock %s\n", __func__,
140 list->name);
141 continue;
142 }
143
144 samsung_clk_add_lookup(clk, list->id);
145 }
146}
147
148/* register a list of mux clocks */
149void __init samsung_clk_register_mux(struct samsung_mux_clock *list,
150 unsigned int nr_clk)
151{
152 struct clk *clk;
153 unsigned int idx, ret;
154
155 for (idx = 0; idx < nr_clk; idx++, list++) {
156 clk = clk_register_mux(NULL, list->name, list->parent_names,
157 list->num_parents, list->flags, reg_base + list->offset,
158 list->shift, list->width, list->mux_flags, &lock);
159 if (IS_ERR(clk)) {
160 pr_err("%s: failed to register clock %s\n", __func__,
161 list->name);
162 continue;
163 }
164
165 samsung_clk_add_lookup(clk, list->id);
166
167 /* register a clock lookup only if a clock alias is specified */
168 if (list->alias) {
169 ret = clk_register_clkdev(clk, list->alias,
170 list->dev_name);
171 if (ret)
172 pr_err("%s: failed to register lookup %s\n",
173 __func__, list->alias);
174 }
175 }
176}
177
178/* register a list of div clocks */
179void __init samsung_clk_register_div(struct samsung_div_clock *list,
180 unsigned int nr_clk)
181{
182 struct clk *clk;
183 unsigned int idx, ret;
184
185 for (idx = 0; idx < nr_clk; idx++, list++) {
186 clk = clk_register_divider(NULL, list->name, list->parent_name,
187 list->flags, reg_base + list->offset, list->shift,
188 list->width, list->div_flags, &lock);
189 if (IS_ERR(clk)) {
190 pr_err("%s: failed to register clock %s\n", __func__,
191 list->name);
192 continue;
193 }
194
195 samsung_clk_add_lookup(clk, list->id);
196
197 /* register a clock lookup only if a clock alias is specified */
198 if (list->alias) {
199 ret = clk_register_clkdev(clk, list->alias,
200 list->dev_name);
201 if (ret)
202 pr_err("%s: failed to register lookup %s\n",
203 __func__, list->alias);
204 }
205 }
206}
207
208/* register a list of gate clocks */
209void __init samsung_clk_register_gate(struct samsung_gate_clock *list,
210 unsigned int nr_clk)
211{
212 struct clk *clk;
213 unsigned int idx, ret;
214
215 for (idx = 0; idx < nr_clk; idx++, list++) {
216 clk = clk_register_gate(NULL, list->name, list->parent_name,
217 list->flags, reg_base + list->offset,
218 list->bit_idx, list->gate_flags, &lock);
219 if (IS_ERR(clk)) {
220 pr_err("%s: failed to register clock %s\n", __func__,
221 list->name);
222 continue;
223 }
224
225 /* register a clock lookup only if a clock alias is specified */
226 if (list->alias) {
227 ret = clk_register_clkdev(clk, list->alias,
228 list->dev_name);
229 if (ret)
230 pr_err("%s: failed to register lookup %s\n",
231 __func__, list->alias);
232 }
233
234 samsung_clk_add_lookup(clk, list->id);
235 }
236}
237
238/*
239 * obtain the clock speed of all external fixed clock sources from device
240 * tree and register it
241 */
242void __init samsung_clk_of_register_fixed_ext(
243 struct samsung_fixed_rate_clock *fixed_rate_clk,
244 unsigned int nr_fixed_rate_clk,
245 struct of_device_id *clk_matches)
246{
247 const struct of_device_id *match;
248 struct device_node *np;
249 u32 freq;
250
251 for_each_matching_node_and_match(np, clk_matches, &match) {
252 if (of_property_read_u32(np, "clock-frequency", &freq))
253 continue;
254 fixed_rate_clk[(u32)match->data].fixed_rate = freq;
255 }
256 samsung_clk_register_fixed_rate(fixed_rate_clk, nr_fixed_rate_clk);
257}
258
259/* utility function to get the rate of a specified clock */
260unsigned long _get_rate(const char *clk_name)
261{
262 struct clk *clk;
263 unsigned long rate;
264
265 clk = clk_get(NULL, clk_name);
266 if (IS_ERR(clk)) {
267 pr_err("%s: could not find clock %s\n", __func__, clk_name);
268 return 0;
269 }
270 rate = clk_get_rate(clk);
271 clk_put(clk);
272 return rate;
273}