blob: 49629c71c9e7769e98e473f784605e877bbf51de [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;
Tomasz Figa91a12632014-02-06 19:33:11 +010057 int i;
58
Rahul Sharma976face2014-03-12 20:26:44 +053059 ctx = kzalloc(sizeof(struct samsung_clk_provider), GFP_KERNEL);
60 if (!ctx)
61 panic("could not allocate clock provider context.\n");
Thomas Abraham721c42a2013-03-09 17:02:44 +090062
Tomasz Figa91a12632014-02-06 19:33:11 +010063 clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL);
Heiko Stueber24661962013-03-18 13:43:52 +090064 if (!clk_table)
65 panic("could not allocate clock lookup table\n");
66
Tomasz Figa91a12632014-02-06 19:33:11 +010067 for (i = 0; i < nr_clks; ++i)
68 clk_table[i] = ERR_PTR(-ENOENT);
69
Rahul Sharma976face2014-03-12 20:26:44 +053070 ctx->reg_base = base;
71 ctx->clk_data.clks = clk_table;
72 ctx->clk_data.clk_num = nr_clks;
73 spin_lock_init(&ctx->lock);
Heiko Stuebner6e92bf5a2013-03-18 13:43:52 +090074
Rahul Sharma976face2014-03-12 20:26:44 +053075 if (!np)
76 return ctx;
77
78 ret = of_clk_add_provider(np, of_clk_src_onecell_get,
79 &ctx->clk_data);
80 if (ret)
81 panic("could not register clock provide\n");
82
83 return ctx;
Thomas Abraham721c42a2013-03-09 17:02:44 +090084}
85
86/* add a clock instance to the clock lookup table used for dt based lookup */
Rahul Sharma976face2014-03-12 20:26:44 +053087void samsung_clk_add_lookup(struct samsung_clk_provider *ctx, struct clk *clk,
88 unsigned int id)
Thomas Abraham721c42a2013-03-09 17:02:44 +090089{
Rahul Sharma976face2014-03-12 20:26:44 +053090 if (ctx->clk_data.clks && id)
91 ctx->clk_data.clks[id] = clk;
Thomas Abraham721c42a2013-03-09 17:02:44 +090092}
93
Heiko Stuebner5e2e0192013-03-18 13:43:56 +090094/* register a list of aliases */
Rahul Sharma976face2014-03-12 20:26:44 +053095void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx,
96 struct samsung_clock_alias *list,
97 unsigned int nr_clk)
Heiko Stuebner5e2e0192013-03-18 13:43:56 +090098{
99 struct clk *clk;
100 unsigned int idx, ret;
101
Rahul Sharma976face2014-03-12 20:26:44 +0530102 if (!ctx->clk_data.clks) {
Heiko Stuebner5e2e0192013-03-18 13:43:56 +0900103 pr_err("%s: clock table missing\n", __func__);
104 return;
105 }
106
107 for (idx = 0; idx < nr_clk; idx++, list++) {
108 if (!list->id) {
109 pr_err("%s: clock id missing for index %d\n", __func__,
110 idx);
111 continue;
112 }
113
Rahul Sharma976face2014-03-12 20:26:44 +0530114 clk = ctx->clk_data.clks[list->id];
Heiko Stuebner5e2e0192013-03-18 13:43:56 +0900115 if (!clk) {
116 pr_err("%s: failed to find clock %d\n", __func__,
117 list->id);
118 continue;
119 }
120
121 ret = clk_register_clkdev(clk, list->alias, list->dev_name);
122 if (ret)
123 pr_err("%s: failed to register lookup %s\n",
124 __func__, list->alias);
125 }
126}
127
Thomas Abraham721c42a2013-03-09 17:02:44 +0900128/* register a list of fixed clocks */
Rahul Sharma976face2014-03-12 20:26:44 +0530129void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx,
Thomas Abraham721c42a2013-03-09 17:02:44 +0900130 struct samsung_fixed_rate_clock *list, unsigned int nr_clk)
131{
132 struct clk *clk;
133 unsigned int idx, ret;
134
135 for (idx = 0; idx < nr_clk; idx++, list++) {
136 clk = clk_register_fixed_rate(NULL, list->name,
137 list->parent_name, list->flags, list->fixed_rate);
138 if (IS_ERR(clk)) {
139 pr_err("%s: failed to register clock %s\n", __func__,
140 list->name);
141 continue;
142 }
143
Rahul Sharma976face2014-03-12 20:26:44 +0530144 samsung_clk_add_lookup(ctx, clk, list->id);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900145
146 /*
147 * Unconditionally add a clock lookup for the fixed rate clocks.
148 * There are not many of these on any of Samsung platforms.
149 */
150 ret = clk_register_clkdev(clk, list->name, NULL);
151 if (ret)
152 pr_err("%s: failed to register clock lookup for %s",
153 __func__, list->name);
154 }
155}
156
157/* register a list of fixed factor clocks */
Rahul Sharma976face2014-03-12 20:26:44 +0530158void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx,
Thomas Abraham721c42a2013-03-09 17:02:44 +0900159 struct samsung_fixed_factor_clock *list, unsigned int nr_clk)
160{
161 struct clk *clk;
162 unsigned int idx;
163
164 for (idx = 0; idx < nr_clk; idx++, list++) {
165 clk = clk_register_fixed_factor(NULL, list->name,
166 list->parent_name, list->flags, list->mult, list->div);
167 if (IS_ERR(clk)) {
168 pr_err("%s: failed to register clock %s\n", __func__,
169 list->name);
170 continue;
171 }
172
Rahul Sharma976face2014-03-12 20:26:44 +0530173 samsung_clk_add_lookup(ctx, clk, list->id);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900174 }
175}
176
177/* register a list of mux clocks */
Rahul Sharma976face2014-03-12 20:26:44 +0530178void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx,
179 struct samsung_mux_clock *list,
180 unsigned int nr_clk)
Thomas Abraham721c42a2013-03-09 17:02:44 +0900181{
182 struct clk *clk;
183 unsigned int idx, ret;
184
185 for (idx = 0; idx < nr_clk; idx++, list++) {
186 clk = clk_register_mux(NULL, list->name, list->parent_names,
Rahul Sharma976face2014-03-12 20:26:44 +0530187 list->num_parents, list->flags,
188 ctx->reg_base + list->offset,
189 list->shift, list->width, list->mux_flags, &ctx->lock);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900190 if (IS_ERR(clk)) {
191 pr_err("%s: failed to register clock %s\n", __func__,
192 list->name);
193 continue;
194 }
195
Rahul Sharma976face2014-03-12 20:26:44 +0530196 samsung_clk_add_lookup(ctx, clk, list->id);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900197
198 /* register a clock lookup only if a clock alias is specified */
199 if (list->alias) {
200 ret = clk_register_clkdev(clk, list->alias,
201 list->dev_name);
202 if (ret)
203 pr_err("%s: failed to register lookup %s\n",
204 __func__, list->alias);
205 }
206 }
207}
208
209/* register a list of div clocks */
Rahul Sharma976face2014-03-12 20:26:44 +0530210void __init samsung_clk_register_div(struct samsung_clk_provider *ctx,
211 struct samsung_div_clock *list,
212 unsigned int nr_clk)
Thomas Abraham721c42a2013-03-09 17:02:44 +0900213{
214 struct clk *clk;
215 unsigned int idx, ret;
216
217 for (idx = 0; idx < nr_clk; idx++, list++) {
Heiko Stuebner798ed612013-03-18 13:43:52 +0900218 if (list->table)
219 clk = clk_register_divider_table(NULL, list->name,
Rahul Sharma976face2014-03-12 20:26:44 +0530220 list->parent_name, list->flags,
221 ctx->reg_base + list->offset,
222 list->shift, list->width, list->div_flags,
223 list->table, &ctx->lock);
Heiko Stuebner798ed612013-03-18 13:43:52 +0900224 else
225 clk = clk_register_divider(NULL, list->name,
Rahul Sharma976face2014-03-12 20:26:44 +0530226 list->parent_name, list->flags,
227 ctx->reg_base + list->offset, list->shift,
228 list->width, list->div_flags, &ctx->lock);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900229 if (IS_ERR(clk)) {
230 pr_err("%s: failed to register clock %s\n", __func__,
231 list->name);
232 continue;
233 }
234
Rahul Sharma976face2014-03-12 20:26:44 +0530235 samsung_clk_add_lookup(ctx, clk, list->id);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900236
237 /* register a clock lookup only if a clock alias is specified */
238 if (list->alias) {
239 ret = clk_register_clkdev(clk, list->alias,
240 list->dev_name);
241 if (ret)
242 pr_err("%s: failed to register lookup %s\n",
243 __func__, list->alias);
244 }
245 }
246}
247
248/* register a list of gate clocks */
Rahul Sharma976face2014-03-12 20:26:44 +0530249void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx,
250 struct samsung_gate_clock *list,
251 unsigned int nr_clk)
Thomas Abraham721c42a2013-03-09 17:02:44 +0900252{
253 struct clk *clk;
254 unsigned int idx, ret;
255
256 for (idx = 0; idx < nr_clk; idx++, list++) {
257 clk = clk_register_gate(NULL, list->name, list->parent_name,
Rahul Sharma976face2014-03-12 20:26:44 +0530258 list->flags, ctx->reg_base + list->offset,
259 list->bit_idx, list->gate_flags, &ctx->lock);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900260 if (IS_ERR(clk)) {
261 pr_err("%s: failed to register clock %s\n", __func__,
262 list->name);
263 continue;
264 }
265
266 /* register a clock lookup only if a clock alias is specified */
267 if (list->alias) {
268 ret = clk_register_clkdev(clk, list->alias,
269 list->dev_name);
270 if (ret)
271 pr_err("%s: failed to register lookup %s\n",
272 __func__, list->alias);
273 }
274
Rahul Sharma976face2014-03-12 20:26:44 +0530275 samsung_clk_add_lookup(ctx, clk, list->id);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900276 }
277}
278
279/*
280 * obtain the clock speed of all external fixed clock sources from device
281 * tree and register it
282 */
Sachin Kamat6cec9082013-04-08 21:35:25 +0900283#ifdef CONFIG_OF
Rahul Sharma976face2014-03-12 20:26:44 +0530284void __init samsung_clk_of_register_fixed_ext(struct samsung_clk_provider *ctx,
Thomas Abraham721c42a2013-03-09 17:02:44 +0900285 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;
Rahul Sharma976face2014-03-12 20:26:44 +0530290 struct device_node *clk_np;
Thomas Abraham721c42a2013-03-09 17:02:44 +0900291 u32 freq;
292
Rahul Sharma976face2014-03-12 20:26:44 +0530293 for_each_matching_node_and_match(clk_np, clk_matches, &match) {
294 if (of_property_read_u32(clk_np, "clock-frequency", &freq))
Thomas Abraham721c42a2013-03-09 17:02:44 +0900295 continue;
Pankaj Dubey42fb57c2014-02-26 11:42:41 +0900296 fixed_rate_clk[(unsigned long)match->data].fixed_rate = freq;
Thomas Abraham721c42a2013-03-09 17:02:44 +0900297 }
Rahul Sharma976face2014-03-12 20:26:44 +0530298 samsung_clk_register_fixed_rate(ctx, fixed_rate_clk, nr_fixed_rate_clk);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900299}
Sachin Kamat6cec9082013-04-08 21:35:25 +0900300#endif
Thomas Abraham721c42a2013-03-09 17:02:44 +0900301
302/* utility function to get the rate of a specified clock */
303unsigned long _get_rate(const char *clk_name)
304{
305 struct clk *clk;
Thomas Abraham721c42a2013-03-09 17:02:44 +0900306
Tomasz Figa3a647892013-08-26 19:09:00 +0200307 clk = __clk_lookup(clk_name);
308 if (!clk) {
Thomas Abraham721c42a2013-03-09 17:02:44 +0900309 pr_err("%s: could not find clock %s\n", __func__, clk_name);
310 return 0;
311 }
Tomasz Figa3a647892013-08-26 19:09:00 +0200312
313 return clk_get_rate(clk);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900314}