blob: 41c1461d2f583030c984ef49745cb50c12b1fc07 [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
Tomasz Figa3ccefbd2014-02-14 08:16:00 +090017void 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
25void 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
Tomasz Figac3b6c1d2014-02-14 08:16:00 +090033struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump(
34 const unsigned long *rdump,
35 unsigned long nr_rdump)
Tomasz Figa3ccefbd2014-02-14 08:16:00 +090036{
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
Thomas Abraham721c42a2013-03-09 17:02:44 +090050/* setup the essentials required to support clock lookup using ccf */
Rahul Sharma976face2014-03-12 20:26:44 +053051struct samsung_clk_provider *__init samsung_clk_init(struct device_node *np,
52 void __iomem *base, unsigned long nr_clks)
Thomas Abraham721c42a2013-03-09 17:02:44 +090053{
Rahul Sharma976face2014-03-12 20:26:44 +053054 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");
Thomas Abraham721c42a2013-03-09 17:02:44 +090060
Heiko Stueber24661962013-03-18 13:43:52 +090061 clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL);
62 if (!clk_table)
63 panic("could not allocate clock lookup table\n");
64
Rahul Sharma976face2014-03-12 20:26:44 +053065 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);
Heiko Stuebner6e92bf5a2013-03-18 13:43:52 +090069
Rahul Sharma976face2014-03-12 20:26:44 +053070 if (!np)
71 return ctx;
72
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;
Thomas Abraham721c42a2013-03-09 17:02:44 +090079}
80
81/* add a clock instance to the clock lookup table used for dt based lookup */
Rahul Sharma976face2014-03-12 20:26:44 +053082void samsung_clk_add_lookup(struct samsung_clk_provider *ctx, struct clk *clk,
83 unsigned int id)
Thomas Abraham721c42a2013-03-09 17:02:44 +090084{
Rahul Sharma976face2014-03-12 20:26:44 +053085 if (ctx->clk_data.clks && id)
86 ctx->clk_data.clks[id] = clk;
Thomas Abraham721c42a2013-03-09 17:02:44 +090087}
88
Heiko Stuebner5e2e0192013-03-18 13:43:56 +090089/* register a list of aliases */
Rahul Sharma976face2014-03-12 20:26:44 +053090void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx,
91 struct samsung_clock_alias *list,
92 unsigned int nr_clk)
Heiko Stuebner5e2e0192013-03-18 13:43:56 +090093{
94 struct clk *clk;
95 unsigned int idx, ret;
96
Rahul Sharma976face2014-03-12 20:26:44 +053097 if (!ctx->clk_data.clks) {
Heiko Stuebner5e2e0192013-03-18 13:43:56 +090098 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
Rahul Sharma976face2014-03-12 20:26:44 +0530109 clk = ctx->clk_data.clks[list->id];
Heiko Stuebner5e2e0192013-03-18 13:43:56 +0900110 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
Thomas Abraham721c42a2013-03-09 17:02:44 +0900123/* register a list of fixed clocks */
Rahul Sharma976face2014-03-12 20:26:44 +0530124void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx,
Thomas Abraham721c42a2013-03-09 17:02:44 +0900125 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
Rahul Sharma976face2014-03-12 20:26:44 +0530139 samsung_clk_add_lookup(ctx, clk, list->id);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900140
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 */
Rahul Sharma976face2014-03-12 20:26:44 +0530153void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx,
Thomas Abraham721c42a2013-03-09 17:02:44 +0900154 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
Rahul Sharma976face2014-03-12 20:26:44 +0530168 samsung_clk_add_lookup(ctx, clk, list->id);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900169 }
170}
171
172/* register a list of mux clocks */
Rahul Sharma976face2014-03-12 20:26:44 +0530173void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx,
174 struct samsung_mux_clock *list,
175 unsigned int nr_clk)
Thomas Abraham721c42a2013-03-09 17:02:44 +0900176{
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,
Rahul Sharma976face2014-03-12 20:26:44 +0530182 list->num_parents, list->flags,
183 ctx->reg_base + list->offset,
184 list->shift, list->width, list->mux_flags, &ctx->lock);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900185 if (IS_ERR(clk)) {
186 pr_err("%s: failed to register clock %s\n", __func__,
187 list->name);
188 continue;
189 }
190
Rahul Sharma976face2014-03-12 20:26:44 +0530191 samsung_clk_add_lookup(ctx, clk, list->id);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900192
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 */
Rahul Sharma976face2014-03-12 20:26:44 +0530205void __init samsung_clk_register_div(struct samsung_clk_provider *ctx,
206 struct samsung_div_clock *list,
207 unsigned int nr_clk)
Thomas Abraham721c42a2013-03-09 17:02:44 +0900208{
209 struct clk *clk;
210 unsigned int idx, ret;
211
212 for (idx = 0; idx < nr_clk; idx++, list++) {
Heiko Stuebner798ed612013-03-18 13:43:52 +0900213 if (list->table)
214 clk = clk_register_divider_table(NULL, list->name,
Rahul Sharma976face2014-03-12 20:26:44 +0530215 list->parent_name, list->flags,
216 ctx->reg_base + list->offset,
217 list->shift, list->width, list->div_flags,
218 list->table, &ctx->lock);
Heiko Stuebner798ed612013-03-18 13:43:52 +0900219 else
220 clk = clk_register_divider(NULL, list->name,
Rahul Sharma976face2014-03-12 20:26:44 +0530221 list->parent_name, list->flags,
222 ctx->reg_base + list->offset, list->shift,
223 list->width, list->div_flags, &ctx->lock);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900224 if (IS_ERR(clk)) {
225 pr_err("%s: failed to register clock %s\n", __func__,
226 list->name);
227 continue;
228 }
229
Rahul Sharma976face2014-03-12 20:26:44 +0530230 samsung_clk_add_lookup(ctx, clk, list->id);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900231
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 */
Rahul Sharma976face2014-03-12 20:26:44 +0530244void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx,
245 struct samsung_gate_clock *list,
246 unsigned int nr_clk)
Thomas Abraham721c42a2013-03-09 17:02:44 +0900247{
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,
Rahul Sharma976face2014-03-12 20:26:44 +0530253 list->flags, ctx->reg_base + list->offset,
254 list->bit_idx, list->gate_flags, &ctx->lock);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900255 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
Rahul Sharma976face2014-03-12 20:26:44 +0530270 samsung_clk_add_lookup(ctx, clk, list->id);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900271 }
272}
273
274/*
275 * obtain the clock speed of all external fixed clock sources from device
276 * tree and register it
277 */
Sachin Kamat6cec9082013-04-08 21:35:25 +0900278#ifdef CONFIG_OF
Rahul Sharma976face2014-03-12 20:26:44 +0530279void __init samsung_clk_of_register_fixed_ext(struct samsung_clk_provider *ctx,
Thomas Abraham721c42a2013-03-09 17:02:44 +0900280 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;
Rahul Sharma976face2014-03-12 20:26:44 +0530285 struct device_node *clk_np;
Thomas Abraham721c42a2013-03-09 17:02:44 +0900286 u32 freq;
287
Rahul Sharma976face2014-03-12 20:26:44 +0530288 for_each_matching_node_and_match(clk_np, clk_matches, &match) {
289 if (of_property_read_u32(clk_np, "clock-frequency", &freq))
Thomas Abraham721c42a2013-03-09 17:02:44 +0900290 continue;
291 fixed_rate_clk[(u32)match->data].fixed_rate = freq;
292 }
Rahul Sharma976face2014-03-12 20:26:44 +0530293 samsung_clk_register_fixed_rate(ctx, fixed_rate_clk, nr_fixed_rate_clk);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900294}
Sachin Kamat6cec9082013-04-08 21:35:25 +0900295#endif
Thomas Abraham721c42a2013-03-09 17:02:44 +0900296
297/* utility function to get the rate of a specified clock */
298unsigned long _get_rate(const char *clk_name)
299{
300 struct clk *clk;
Thomas Abraham721c42a2013-03-09 17:02:44 +0900301
Tomasz Figa3a647892013-08-26 19:09:00 +0200302 clk = __clk_lookup(clk_name);
303 if (!clk) {
Thomas Abraham721c42a2013-03-09 17:02:44 +0900304 pr_err("%s: could not find clock %s\n", __func__, clk_name);
305 return 0;
306 }
Tomasz Figa3a647892013-08-26 19:09:00 +0200307
308 return clk_get_rate(clk);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900309}