blob: 91d12f397f5d70e8bfee251fb32d609c199f809e [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;
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}