]>
Commit | Line | Data |
---|---|---|
721c42a3 TA |
1 | /* |
2 | * Copyright (c) 2013 Samsung Electronics Co., Ltd. | |
3 | * Copyright (c) 2013 Linaro Ltd. | |
4 | * Author: Thomas Abraham <[email protected]> | |
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 | ||
3ccefbd2 TF |
17 | void samsung_clk_save(void __iomem *base, |
18 | struct samsung_clk_reg_dump *rd, | |
19 | unsigned int num_regs) | |
20 | { | |
21 | for (; num_regs > 0; --num_regs, ++rd) | |
22 | rd->value = readl(base + rd->offset); | |
23 | } | |
24 | ||
25 | void samsung_clk_restore(void __iomem *base, | |
26 | const struct samsung_clk_reg_dump *rd, | |
27 | unsigned int num_regs) | |
28 | { | |
29 | for (; num_regs > 0; --num_regs, ++rd) | |
30 | writel(rd->value, base + rd->offset); | |
31 | } | |
32 | ||
c3b6c1d7 TF |
33 | struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump( |
34 | const unsigned long *rdump, | |
35 | unsigned long nr_rdump) | |
3ccefbd2 TF |
36 | { |
37 | struct samsung_clk_reg_dump *rd; | |
38 | unsigned int i; | |
39 | ||
40 | rd = kcalloc(nr_rdump, sizeof(*rd), GFP_KERNEL); | |
41 | if (!rd) | |
42 | return NULL; | |
43 | ||
44 | for (i = 0; i < nr_rdump; ++i) | |
45 | rd[i].offset = rdump[i]; | |
46 | ||
47 | return rd; | |
48 | } | |
49 | ||
721c42a3 | 50 | /* setup the essentials required to support clock lookup using ccf */ |
976face4 RS |
51 | struct samsung_clk_provider *__init samsung_clk_init(struct device_node *np, |
52 | void __iomem *base, unsigned long nr_clks) | |
721c42a3 | 53 | { |
976face4 RS |
54 | struct samsung_clk_provider *ctx; |
55 | struct clk **clk_table; | |
56 | int ret; | |
57 | ctx = kzalloc(sizeof(struct samsung_clk_provider), GFP_KERNEL); | |
58 | if (!ctx) | |
59 | panic("could not allocate clock provider context.\n"); | |
721c42a3 | 60 | |
2466196d HS |
61 | clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL); |
62 | if (!clk_table) | |
63 | panic("could not allocate clock lookup table\n"); | |
64 | ||
976face4 RS |
65 | ctx->reg_base = base; |
66 | ctx->clk_data.clks = clk_table; | |
67 | ctx->clk_data.clk_num = nr_clks; | |
68 | spin_lock_init(&ctx->lock); | |
69 | ||
6e92bf5a | 70 | if (!np) |
976face4 | 71 | return ctx; |
6e92bf5a | 72 | |
976face4 RS |
73 | ret = of_clk_add_provider(np, of_clk_src_onecell_get, |
74 | &ctx->clk_data); | |
75 | if (ret) | |
76 | panic("could not register clock provide\n"); | |
77 | ||
78 | return ctx; | |
721c42a3 TA |
79 | } |
80 | ||
81 | /* add a clock instance to the clock lookup table used for dt based lookup */ | |
976face4 RS |
82 | void samsung_clk_add_lookup(struct samsung_clk_provider *ctx, struct clk *clk, |
83 | unsigned int id) | |
721c42a3 | 84 | { |
976face4 RS |
85 | if (ctx->clk_data.clks && id) |
86 | ctx->clk_data.clks[id] = clk; | |
721c42a3 TA |
87 | } |
88 | ||
5e2e0195 | 89 | /* register a list of aliases */ |
976face4 RS |
90 | void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx, |
91 | struct samsung_clock_alias *list, | |
92 | unsigned int nr_clk) | |
5e2e0195 HS |
93 | { |
94 | struct clk *clk; | |
95 | unsigned int idx, ret; | |
96 | ||
976face4 | 97 | if (!ctx->clk_data.clks) { |
5e2e0195 HS |
98 | pr_err("%s: clock table missing\n", __func__); |
99 | return; | |
100 | } | |
101 | ||
102 | for (idx = 0; idx < nr_clk; idx++, list++) { | |
103 | if (!list->id) { | |
104 | pr_err("%s: clock id missing for index %d\n", __func__, | |
105 | idx); | |
106 | continue; | |
107 | } | |
108 | ||
976face4 | 109 | clk = ctx->clk_data.clks[list->id]; |
5e2e0195 HS |
110 | if (!clk) { |
111 | pr_err("%s: failed to find clock %d\n", __func__, | |
112 | list->id); | |
113 | continue; | |
114 | } | |
115 | ||
116 | ret = clk_register_clkdev(clk, list->alias, list->dev_name); | |
117 | if (ret) | |
118 | pr_err("%s: failed to register lookup %s\n", | |
119 | __func__, list->alias); | |
120 | } | |
121 | } | |
122 | ||
721c42a3 | 123 | /* register a list of fixed clocks */ |
976face4 | 124 | void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx, |
721c42a3 TA |
125 | struct samsung_fixed_rate_clock *list, unsigned int nr_clk) |
126 | { | |
127 | struct clk *clk; | |
128 | unsigned int idx, ret; | |
129 | ||
130 | for (idx = 0; idx < nr_clk; idx++, list++) { | |
131 | clk = clk_register_fixed_rate(NULL, list->name, | |
132 | list->parent_name, list->flags, list->fixed_rate); | |
133 | if (IS_ERR(clk)) { | |
134 | pr_err("%s: failed to register clock %s\n", __func__, | |
135 | list->name); | |
136 | continue; | |
137 | } | |
138 | ||
976face4 | 139 | samsung_clk_add_lookup(ctx, clk, list->id); |
721c42a3 TA |
140 | |
141 | /* | |
142 | * Unconditionally add a clock lookup for the fixed rate clocks. | |
143 | * There are not many of these on any of Samsung platforms. | |
144 | */ | |
145 | ret = clk_register_clkdev(clk, list->name, NULL); | |
146 | if (ret) | |
147 | pr_err("%s: failed to register clock lookup for %s", | |
148 | __func__, list->name); | |
149 | } | |
150 | } | |
151 | ||
152 | /* register a list of fixed factor clocks */ | |
976face4 | 153 | void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx, |
721c42a3 TA |
154 | struct samsung_fixed_factor_clock *list, unsigned int nr_clk) |
155 | { | |
156 | struct clk *clk; | |
157 | unsigned int idx; | |
158 | ||
159 | for (idx = 0; idx < nr_clk; idx++, list++) { | |
160 | clk = clk_register_fixed_factor(NULL, list->name, | |
161 | list->parent_name, list->flags, list->mult, list->div); | |
162 | if (IS_ERR(clk)) { | |
163 | pr_err("%s: failed to register clock %s\n", __func__, | |
164 | list->name); | |
165 | continue; | |
166 | } | |
167 | ||
976face4 | 168 | samsung_clk_add_lookup(ctx, clk, list->id); |
721c42a3 TA |
169 | } |
170 | } | |
171 | ||
172 | /* register a list of mux clocks */ | |
976face4 RS |
173 | void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx, |
174 | struct samsung_mux_clock *list, | |
175 | unsigned int nr_clk) | |
721c42a3 TA |
176 | { |
177 | struct clk *clk; | |
178 | unsigned int idx, ret; | |
179 | ||
180 | for (idx = 0; idx < nr_clk; idx++, list++) { | |
181 | clk = clk_register_mux(NULL, list->name, list->parent_names, | |
976face4 RS |
182 | list->num_parents, list->flags, |
183 | ctx->reg_base + list->offset, | |
184 | list->shift, list->width, list->mux_flags, &ctx->lock); | |
721c42a3 TA |
185 | if (IS_ERR(clk)) { |
186 | pr_err("%s: failed to register clock %s\n", __func__, | |
187 | list->name); | |
188 | continue; | |
189 | } | |
190 | ||
976face4 | 191 | samsung_clk_add_lookup(ctx, clk, list->id); |
721c42a3 TA |
192 | |
193 | /* register a clock lookup only if a clock alias is specified */ | |
194 | if (list->alias) { | |
195 | ret = clk_register_clkdev(clk, list->alias, | |
196 | list->dev_name); | |
197 | if (ret) | |
198 | pr_err("%s: failed to register lookup %s\n", | |
199 | __func__, list->alias); | |
200 | } | |
201 | } | |
202 | } | |
203 | ||
204 | /* register a list of div clocks */ | |
976face4 RS |
205 | void __init samsung_clk_register_div(struct samsung_clk_provider *ctx, |
206 | struct samsung_div_clock *list, | |
207 | unsigned int nr_clk) | |
721c42a3 TA |
208 | { |
209 | struct clk *clk; | |
210 | unsigned int idx, ret; | |
211 | ||
212 | for (idx = 0; idx < nr_clk; idx++, list++) { | |
798ed613 HS |
213 | if (list->table) |
214 | clk = clk_register_divider_table(NULL, list->name, | |
976face4 RS |
215 | list->parent_name, list->flags, |
216 | ctx->reg_base + list->offset, | |
217 | list->shift, list->width, list->div_flags, | |
218 | list->table, &ctx->lock); | |
798ed613 HS |
219 | else |
220 | clk = clk_register_divider(NULL, list->name, | |
976face4 RS |
221 | list->parent_name, list->flags, |
222 | ctx->reg_base + list->offset, list->shift, | |
223 | list->width, list->div_flags, &ctx->lock); | |
721c42a3 TA |
224 | if (IS_ERR(clk)) { |
225 | pr_err("%s: failed to register clock %s\n", __func__, | |
226 | list->name); | |
227 | continue; | |
228 | } | |
229 | ||
976face4 | 230 | samsung_clk_add_lookup(ctx, clk, list->id); |
721c42a3 TA |
231 | |
232 | /* register a clock lookup only if a clock alias is specified */ | |
233 | if (list->alias) { | |
234 | ret = clk_register_clkdev(clk, list->alias, | |
235 | list->dev_name); | |
236 | if (ret) | |
237 | pr_err("%s: failed to register lookup %s\n", | |
238 | __func__, list->alias); | |
239 | } | |
240 | } | |
241 | } | |
242 | ||
243 | /* register a list of gate clocks */ | |
976face4 RS |
244 | void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx, |
245 | struct samsung_gate_clock *list, | |
246 | unsigned int nr_clk) | |
721c42a3 TA |
247 | { |
248 | struct clk *clk; | |
249 | unsigned int idx, ret; | |
250 | ||
251 | for (idx = 0; idx < nr_clk; idx++, list++) { | |
252 | clk = clk_register_gate(NULL, list->name, list->parent_name, | |
976face4 RS |
253 | list->flags, ctx->reg_base + list->offset, |
254 | list->bit_idx, list->gate_flags, &ctx->lock); | |
721c42a3 TA |
255 | if (IS_ERR(clk)) { |
256 | pr_err("%s: failed to register clock %s\n", __func__, | |
257 | list->name); | |
258 | continue; | |
259 | } | |
260 | ||
261 | /* register a clock lookup only if a clock alias is specified */ | |
262 | if (list->alias) { | |
263 | ret = clk_register_clkdev(clk, list->alias, | |
264 | list->dev_name); | |
265 | if (ret) | |
266 | pr_err("%s: failed to register lookup %s\n", | |
267 | __func__, list->alias); | |
268 | } | |
269 | ||
976face4 | 270 | samsung_clk_add_lookup(ctx, clk, list->id); |
721c42a3 TA |
271 | } |
272 | } | |
273 | ||
274 | /* | |
275 | * obtain the clock speed of all external fixed clock sources from device | |
276 | * tree and register it | |
277 | */ | |
6cec9082 | 278 | #ifdef CONFIG_OF |
976face4 | 279 | void __init samsung_clk_of_register_fixed_ext(struct samsung_clk_provider *ctx, |
721c42a3 TA |
280 | struct samsung_fixed_rate_clock *fixed_rate_clk, |
281 | unsigned int nr_fixed_rate_clk, | |
282 | struct of_device_id *clk_matches) | |
283 | { | |
284 | const struct of_device_id *match; | |
976face4 | 285 | struct device_node *clk_np; |
721c42a3 TA |
286 | u32 freq; |
287 | ||
976face4 RS |
288 | for_each_matching_node_and_match(clk_np, clk_matches, &match) { |
289 | if (of_property_read_u32(clk_np, "clock-frequency", &freq)) | |
721c42a3 TA |
290 | continue; |
291 | fixed_rate_clk[(u32)match->data].fixed_rate = freq; | |
292 | } | |
976face4 | 293 | samsung_clk_register_fixed_rate(ctx, fixed_rate_clk, nr_fixed_rate_clk); |
721c42a3 | 294 | } |
6cec9082 | 295 | #endif |
721c42a3 TA |
296 | |
297 | /* utility function to get the rate of a specified clock */ | |
298 | unsigned long _get_rate(const char *clk_name) | |
299 | { | |
300 | struct clk *clk; | |
721c42a3 | 301 | |
3a647895 TF |
302 | clk = __clk_lookup(clk_name); |
303 | if (!clk) { | |
721c42a3 TA |
304 | pr_err("%s: could not find clock %s\n", __func__, clk_name); |
305 | return 0; | |
306 | } | |
3a647895 TF |
307 | |
308 | return clk_get_rate(clk); | |
721c42a3 | 309 | } |