blob: 82f27f644dae584eeb51cddbebb2c224ff4bc288 [file] [log] [blame]
Thomas Abraham721c42a2013-03-09 17:02:44 +09001/*
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;
Thomas Abraham721c42a2013-03-09 17:02:44 +090060
61#ifdef CONFIG_PM_SLEEP
62 if (rdump && nr_rdump) {
63 unsigned int idx;
64 reg_dump = kzalloc(sizeof(struct samsung_clk_reg_dump)
65 * nr_rdump, GFP_KERNEL);
66 if (!reg_dump) {
67 pr_err("%s: memory alloc for register dump failed\n",
68 __func__);
69 return;
70 }
71
72 for (idx = 0; idx < nr_rdump; idx++)
73 reg_dump[idx].offset = rdump[idx];
74 nr_reg_dump = nr_rdump;
75 register_syscore_ops(&samsung_clk_syscore_ops);
76 }
77#endif
Heiko Stueber24661962013-03-18 13:43:52 +090078
Heiko Stueber24661962013-03-18 13:43:52 +090079 clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL);
80 if (!clk_table)
81 panic("could not allocate clock lookup table\n");
82
Heiko Stuebner6e92bf5a2013-03-18 13:43:52 +090083 if (!np)
84 return;
85
86#ifdef CONFIG_OF
Heiko Stueber24661962013-03-18 13:43:52 +090087 clk_data.clks = clk_table;
88 clk_data.clk_num = nr_clks;
89 of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
90#endif
Thomas Abraham721c42a2013-03-09 17:02:44 +090091}
92
93/* add a clock instance to the clock lookup table used for dt based lookup */
94void samsung_clk_add_lookup(struct clk *clk, unsigned int id)
95{
96 if (clk_table && id)
97 clk_table[id] = clk;
98}
99
Heiko Stuebner5e2e0192013-03-18 13:43:56 +0900100/* register a list of aliases */
101void __init samsung_clk_register_alias(struct samsung_clock_alias *list,
102 unsigned int nr_clk)
103{
104 struct clk *clk;
105 unsigned int idx, ret;
106
107 if (!clk_table) {
108 pr_err("%s: clock table missing\n", __func__);
109 return;
110 }
111
112 for (idx = 0; idx < nr_clk; idx++, list++) {
113 if (!list->id) {
114 pr_err("%s: clock id missing for index %d\n", __func__,
115 idx);
116 continue;
117 }
118
119 clk = clk_table[list->id];
120 if (!clk) {
121 pr_err("%s: failed to find clock %d\n", __func__,
122 list->id);
123 continue;
124 }
125
126 ret = clk_register_clkdev(clk, list->alias, list->dev_name);
127 if (ret)
128 pr_err("%s: failed to register lookup %s\n",
129 __func__, list->alias);
130 }
131}
132
Thomas Abraham721c42a2013-03-09 17:02:44 +0900133/* register a list of fixed clocks */
134void __init samsung_clk_register_fixed_rate(
135 struct samsung_fixed_rate_clock *list, unsigned int nr_clk)
136{
137 struct clk *clk;
138 unsigned int idx, ret;
139
140 for (idx = 0; idx < nr_clk; idx++, list++) {
141 clk = clk_register_fixed_rate(NULL, list->name,
142 list->parent_name, list->flags, list->fixed_rate);
143 if (IS_ERR(clk)) {
144 pr_err("%s: failed to register clock %s\n", __func__,
145 list->name);
146 continue;
147 }
148
149 samsung_clk_add_lookup(clk, list->id);
150
151 /*
152 * Unconditionally add a clock lookup for the fixed rate clocks.
153 * There are not many of these on any of Samsung platforms.
154 */
155 ret = clk_register_clkdev(clk, list->name, NULL);
156 if (ret)
157 pr_err("%s: failed to register clock lookup for %s",
158 __func__, list->name);
159 }
160}
161
162/* register a list of fixed factor clocks */
163void __init samsung_clk_register_fixed_factor(
164 struct samsung_fixed_factor_clock *list, unsigned int nr_clk)
165{
166 struct clk *clk;
167 unsigned int idx;
168
169 for (idx = 0; idx < nr_clk; idx++, list++) {
170 clk = clk_register_fixed_factor(NULL, list->name,
171 list->parent_name, list->flags, list->mult, list->div);
172 if (IS_ERR(clk)) {
173 pr_err("%s: failed to register clock %s\n", __func__,
174 list->name);
175 continue;
176 }
177
178 samsung_clk_add_lookup(clk, list->id);
179 }
180}
181
182/* register a list of mux clocks */
183void __init samsung_clk_register_mux(struct samsung_mux_clock *list,
184 unsigned int nr_clk)
185{
186 struct clk *clk;
187 unsigned int idx, ret;
188
189 for (idx = 0; idx < nr_clk; idx++, list++) {
190 clk = clk_register_mux(NULL, list->name, list->parent_names,
191 list->num_parents, list->flags, reg_base + list->offset,
192 list->shift, list->width, list->mux_flags, &lock);
193 if (IS_ERR(clk)) {
194 pr_err("%s: failed to register clock %s\n", __func__,
195 list->name);
196 continue;
197 }
198
199 samsung_clk_add_lookup(clk, list->id);
200
201 /* register a clock lookup only if a clock alias is specified */
202 if (list->alias) {
203 ret = clk_register_clkdev(clk, list->alias,
204 list->dev_name);
205 if (ret)
206 pr_err("%s: failed to register lookup %s\n",
207 __func__, list->alias);
208 }
209 }
210}
211
212/* register a list of div clocks */
213void __init samsung_clk_register_div(struct samsung_div_clock *list,
214 unsigned int nr_clk)
215{
216 struct clk *clk;
217 unsigned int idx, ret;
218
219 for (idx = 0; idx < nr_clk; idx++, list++) {
Heiko Stuebner798ed612013-03-18 13:43:52 +0900220 if (list->table)
221 clk = clk_register_divider_table(NULL, list->name,
222 list->parent_name, list->flags,
223 reg_base + list->offset, list->shift,
224 list->width, list->div_flags,
225 list->table, &lock);
226 else
227 clk = clk_register_divider(NULL, list->name,
228 list->parent_name, list->flags,
229 reg_base + list->offset, list->shift,
230 list->width, list->div_flags, &lock);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900231 if (IS_ERR(clk)) {
232 pr_err("%s: failed to register clock %s\n", __func__,
233 list->name);
234 continue;
235 }
236
237 samsung_clk_add_lookup(clk, list->id);
238
239 /* register a clock lookup only if a clock alias is specified */
240 if (list->alias) {
241 ret = clk_register_clkdev(clk, list->alias,
242 list->dev_name);
243 if (ret)
244 pr_err("%s: failed to register lookup %s\n",
245 __func__, list->alias);
246 }
247 }
248}
249
250/* register a list of gate clocks */
251void __init samsung_clk_register_gate(struct samsung_gate_clock *list,
252 unsigned int nr_clk)
253{
254 struct clk *clk;
255 unsigned int idx, ret;
256
257 for (idx = 0; idx < nr_clk; idx++, list++) {
258 clk = clk_register_gate(NULL, list->name, list->parent_name,
259 list->flags, reg_base + list->offset,
260 list->bit_idx, list->gate_flags, &lock);
261 if (IS_ERR(clk)) {
262 pr_err("%s: failed to register clock %s\n", __func__,
263 list->name);
264 continue;
265 }
266
267 /* register a clock lookup only if a clock alias is specified */
268 if (list->alias) {
269 ret = clk_register_clkdev(clk, list->alias,
270 list->dev_name);
271 if (ret)
272 pr_err("%s: failed to register lookup %s\n",
273 __func__, list->alias);
274 }
275
276 samsung_clk_add_lookup(clk, list->id);
277 }
278}
279
280/*
281 * obtain the clock speed of all external fixed clock sources from device
282 * tree and register it
283 */
284void __init samsung_clk_of_register_fixed_ext(
285 struct samsung_fixed_rate_clock *fixed_rate_clk,
286 unsigned int nr_fixed_rate_clk,
287 struct of_device_id *clk_matches)
288{
289 const struct of_device_id *match;
290 struct device_node *np;
291 u32 freq;
292
293 for_each_matching_node_and_match(np, clk_matches, &match) {
294 if (of_property_read_u32(np, "clock-frequency", &freq))
295 continue;
296 fixed_rate_clk[(u32)match->data].fixed_rate = freq;
297 }
298 samsung_clk_register_fixed_rate(fixed_rate_clk, nr_fixed_rate_clk);
299}
300
301/* utility function to get the rate of a specified clock */
302unsigned long _get_rate(const char *clk_name)
303{
304 struct clk *clk;
305 unsigned long rate;
306
307 clk = clk_get(NULL, clk_name);
308 if (IS_ERR(clk)) {
309 pr_err("%s: could not find clock %s\n", __func__, clk_name);
310 return 0;
311 }
312 rate = clk_get_rate(clk);
313 clk_put(clk);
314 return rate;
315}