blob: d36cdd511761952b66dc06147abe95857e5f600e [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++) {
Heiko Stuebner798ed612013-03-18 13:43:52 +0900186 if (list->table)
187 clk = clk_register_divider_table(NULL, list->name,
188 list->parent_name, list->flags,
189 reg_base + list->offset, list->shift,
190 list->width, list->div_flags,
191 list->table, &lock);
192 else
193 clk = clk_register_divider(NULL, list->name,
194 list->parent_name, list->flags,
195 reg_base + list->offset, list->shift,
196 list->width, list->div_flags, &lock);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900197 if (IS_ERR(clk)) {
198 pr_err("%s: failed to register clock %s\n", __func__,
199 list->name);
200 continue;
201 }
202
203 samsung_clk_add_lookup(clk, list->id);
204
205 /* register a clock lookup only if a clock alias is specified */
206 if (list->alias) {
207 ret = clk_register_clkdev(clk, list->alias,
208 list->dev_name);
209 if (ret)
210 pr_err("%s: failed to register lookup %s\n",
211 __func__, list->alias);
212 }
213 }
214}
215
216/* register a list of gate clocks */
217void __init samsung_clk_register_gate(struct samsung_gate_clock *list,
218 unsigned int nr_clk)
219{
220 struct clk *clk;
221 unsigned int idx, ret;
222
223 for (idx = 0; idx < nr_clk; idx++, list++) {
224 clk = clk_register_gate(NULL, list->name, list->parent_name,
225 list->flags, reg_base + list->offset,
226 list->bit_idx, list->gate_flags, &lock);
227 if (IS_ERR(clk)) {
228 pr_err("%s: failed to register clock %s\n", __func__,
229 list->name);
230 continue;
231 }
232
233 /* register a clock lookup only if a clock alias is specified */
234 if (list->alias) {
235 ret = clk_register_clkdev(clk, list->alias,
236 list->dev_name);
237 if (ret)
238 pr_err("%s: failed to register lookup %s\n",
239 __func__, list->alias);
240 }
241
242 samsung_clk_add_lookup(clk, list->id);
243 }
244}
245
246/*
247 * obtain the clock speed of all external fixed clock sources from device
248 * tree and register it
249 */
250void __init samsung_clk_of_register_fixed_ext(
251 struct samsung_fixed_rate_clock *fixed_rate_clk,
252 unsigned int nr_fixed_rate_clk,
253 struct of_device_id *clk_matches)
254{
255 const struct of_device_id *match;
256 struct device_node *np;
257 u32 freq;
258
259 for_each_matching_node_and_match(np, clk_matches, &match) {
260 if (of_property_read_u32(np, "clock-frequency", &freq))
261 continue;
262 fixed_rate_clk[(u32)match->data].fixed_rate = freq;
263 }
264 samsung_clk_register_fixed_rate(fixed_rate_clk, nr_fixed_rate_clk);
265}
266
267/* utility function to get the rate of a specified clock */
268unsigned long _get_rate(const char *clk_name)
269{
270 struct clk *clk;
271 unsigned long rate;
272
273 clk = clk_get(NULL, clk_name);
274 if (IS_ERR(clk)) {
275 pr_err("%s: could not find clock %s\n", __func__, clk_name);
276 return 0;
277 }
278 rate = clk_get_rate(clk);
279 clk_put(clk);
280 return rate;
281}