blob: deab84d9f37deec161070fc372f6756a90369a43 [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;
Tomasz Figa91a12632014-02-06 19:33:11 +010056 int i;
57
Rahul Sharma976face2014-03-12 20:26:44 +053058 ctx = kzalloc(sizeof(struct samsung_clk_provider), GFP_KERNEL);
59 if (!ctx)
60 panic("could not allocate clock provider context.\n");
Thomas Abraham721c42a2013-03-09 17:02:44 +090061
Tomasz Figa91a12632014-02-06 19:33:11 +010062 clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL);
Heiko Stueber24661962013-03-18 13:43:52 +090063 if (!clk_table)
64 panic("could not allocate clock lookup table\n");
65
Tomasz Figa91a12632014-02-06 19:33:11 +010066 for (i = 0; i < nr_clks; ++i)
67 clk_table[i] = ERR_PTR(-ENOENT);
68
Rahul Sharma976face2014-03-12 20:26:44 +053069 ctx->reg_base = base;
70 ctx->clk_data.clks = clk_table;
71 ctx->clk_data.clk_num = nr_clks;
72 spin_lock_init(&ctx->lock);
Heiko Stuebner6e92bf5a2013-03-18 13:43:52 +090073
Rahul Sharma976face2014-03-12 20:26:44 +053074 return ctx;
Thomas Abraham721c42a2013-03-09 17:02:44 +090075}
76
Sylwester Nawrockid5e136a2014-06-18 17:46:52 +020077void __init samsung_clk_of_add_provider(struct device_node *np,
78 struct samsung_clk_provider *ctx)
79{
80 if (np) {
81 if (of_clk_add_provider(np, of_clk_src_onecell_get,
82 &ctx->clk_data))
83 panic("could not register clk provider\n");
84 }
85}
86
Thomas Abraham721c42a2013-03-09 17:02:44 +090087/* add a clock instance to the clock lookup table used for dt based lookup */
Rahul Sharma976face2014-03-12 20:26:44 +053088void samsung_clk_add_lookup(struct samsung_clk_provider *ctx, struct clk *clk,
89 unsigned int id)
Thomas Abraham721c42a2013-03-09 17:02:44 +090090{
Rahul Sharma976face2014-03-12 20:26:44 +053091 if (ctx->clk_data.clks && id)
92 ctx->clk_data.clks[id] = clk;
Thomas Abraham721c42a2013-03-09 17:02:44 +090093}
94
Heiko Stuebner5e2e0192013-03-18 13:43:56 +090095/* register a list of aliases */
Rahul Sharma976face2014-03-12 20:26:44 +053096void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx,
97 struct samsung_clock_alias *list,
98 unsigned int nr_clk)
Heiko Stuebner5e2e0192013-03-18 13:43:56 +090099{
100 struct clk *clk;
101 unsigned int idx, ret;
102
Rahul Sharma976face2014-03-12 20:26:44 +0530103 if (!ctx->clk_data.clks) {
Heiko Stuebner5e2e0192013-03-18 13:43:56 +0900104 pr_err("%s: clock table missing\n", __func__);
105 return;
106 }
107
108 for (idx = 0; idx < nr_clk; idx++, list++) {
109 if (!list->id) {
110 pr_err("%s: clock id missing for index %d\n", __func__,
111 idx);
112 continue;
113 }
114
Rahul Sharma976face2014-03-12 20:26:44 +0530115 clk = ctx->clk_data.clks[list->id];
Heiko Stuebner5e2e0192013-03-18 13:43:56 +0900116 if (!clk) {
117 pr_err("%s: failed to find clock %d\n", __func__,
118 list->id);
119 continue;
120 }
121
122 ret = clk_register_clkdev(clk, list->alias, list->dev_name);
123 if (ret)
124 pr_err("%s: failed to register lookup %s\n",
125 __func__, list->alias);
126 }
127}
128
Thomas Abraham721c42a2013-03-09 17:02:44 +0900129/* register a list of fixed clocks */
Rahul Sharma976face2014-03-12 20:26:44 +0530130void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx,
Thomas Abraham721c42a2013-03-09 17:02:44 +0900131 struct samsung_fixed_rate_clock *list, unsigned int nr_clk)
132{
133 struct clk *clk;
134 unsigned int idx, ret;
135
136 for (idx = 0; idx < nr_clk; idx++, list++) {
137 clk = clk_register_fixed_rate(NULL, list->name,
138 list->parent_name, list->flags, list->fixed_rate);
139 if (IS_ERR(clk)) {
140 pr_err("%s: failed to register clock %s\n", __func__,
141 list->name);
142 continue;
143 }
144
Rahul Sharma976face2014-03-12 20:26:44 +0530145 samsung_clk_add_lookup(ctx, clk, list->id);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900146
147 /*
148 * Unconditionally add a clock lookup for the fixed rate clocks.
149 * There are not many of these on any of Samsung platforms.
150 */
151 ret = clk_register_clkdev(clk, list->name, NULL);
152 if (ret)
153 pr_err("%s: failed to register clock lookup for %s",
154 __func__, list->name);
155 }
156}
157
158/* register a list of fixed factor clocks */
Rahul Sharma976face2014-03-12 20:26:44 +0530159void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx,
Thomas Abraham721c42a2013-03-09 17:02:44 +0900160 struct samsung_fixed_factor_clock *list, unsigned int nr_clk)
161{
162 struct clk *clk;
163 unsigned int idx;
164
165 for (idx = 0; idx < nr_clk; idx++, list++) {
166 clk = clk_register_fixed_factor(NULL, list->name,
167 list->parent_name, list->flags, list->mult, list->div);
168 if (IS_ERR(clk)) {
169 pr_err("%s: failed to register clock %s\n", __func__,
170 list->name);
171 continue;
172 }
173
Rahul Sharma976face2014-03-12 20:26:44 +0530174 samsung_clk_add_lookup(ctx, clk, list->id);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900175 }
176}
177
178/* register a list of mux clocks */
Rahul Sharma976face2014-03-12 20:26:44 +0530179void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx,
180 struct samsung_mux_clock *list,
181 unsigned int nr_clk)
Thomas Abraham721c42a2013-03-09 17:02:44 +0900182{
183 struct clk *clk;
184 unsigned int idx, ret;
185
186 for (idx = 0; idx < nr_clk; idx++, list++) {
187 clk = clk_register_mux(NULL, list->name, list->parent_names,
Rahul Sharma976face2014-03-12 20:26:44 +0530188 list->num_parents, list->flags,
189 ctx->reg_base + list->offset,
190 list->shift, list->width, list->mux_flags, &ctx->lock);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900191 if (IS_ERR(clk)) {
192 pr_err("%s: failed to register clock %s\n", __func__,
193 list->name);
194 continue;
195 }
196
Rahul Sharma976face2014-03-12 20:26:44 +0530197 samsung_clk_add_lookup(ctx, clk, list->id);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900198
199 /* register a clock lookup only if a clock alias is specified */
200 if (list->alias) {
201 ret = clk_register_clkdev(clk, list->alias,
202 list->dev_name);
203 if (ret)
204 pr_err("%s: failed to register lookup %s\n",
205 __func__, list->alias);
206 }
207 }
208}
209
210/* register a list of div clocks */
Rahul Sharma976face2014-03-12 20:26:44 +0530211void __init samsung_clk_register_div(struct samsung_clk_provider *ctx,
212 struct samsung_div_clock *list,
213 unsigned int nr_clk)
Thomas Abraham721c42a2013-03-09 17:02:44 +0900214{
215 struct clk *clk;
216 unsigned int idx, ret;
217
218 for (idx = 0; idx < nr_clk; idx++, list++) {
Heiko Stuebner798ed612013-03-18 13:43:52 +0900219 if (list->table)
220 clk = clk_register_divider_table(NULL, list->name,
Rahul Sharma976face2014-03-12 20:26:44 +0530221 list->parent_name, list->flags,
222 ctx->reg_base + list->offset,
223 list->shift, list->width, list->div_flags,
224 list->table, &ctx->lock);
Heiko Stuebner798ed612013-03-18 13:43:52 +0900225 else
226 clk = clk_register_divider(NULL, list->name,
Rahul Sharma976face2014-03-12 20:26:44 +0530227 list->parent_name, list->flags,
228 ctx->reg_base + list->offset, list->shift,
229 list->width, list->div_flags, &ctx->lock);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900230 if (IS_ERR(clk)) {
231 pr_err("%s: failed to register clock %s\n", __func__,
232 list->name);
233 continue;
234 }
235
Rahul Sharma976face2014-03-12 20:26:44 +0530236 samsung_clk_add_lookup(ctx, clk, list->id);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900237
238 /* register a clock lookup only if a clock alias is specified */
239 if (list->alias) {
240 ret = clk_register_clkdev(clk, list->alias,
241 list->dev_name);
242 if (ret)
243 pr_err("%s: failed to register lookup %s\n",
244 __func__, list->alias);
245 }
246 }
247}
248
249/* register a list of gate clocks */
Rahul Sharma976face2014-03-12 20:26:44 +0530250void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx,
251 struct samsung_gate_clock *list,
252 unsigned int nr_clk)
Thomas Abraham721c42a2013-03-09 17:02:44 +0900253{
254 struct clk *clk;
255 unsigned int idx, ret;
256
257 for (idx = 0; idx < nr_clk; idx++, list++) {
258 clk = clk_register_gate(NULL, list->name, list->parent_name,
Rahul Sharma976face2014-03-12 20:26:44 +0530259 list->flags, ctx->reg_base + list->offset,
260 list->bit_idx, list->gate_flags, &ctx->lock);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900261 if (IS_ERR(clk)) {
262 pr_err("%s: failed to register clock %s\n", __func__,
263 list->name);
264 continue;
265 }
266
267 /* register a clock lookup only if a clock alias is specified */
268 if (list->alias) {
269 ret = clk_register_clkdev(clk, list->alias,
270 list->dev_name);
271 if (ret)
272 pr_err("%s: failed to register lookup %s\n",
273 __func__, list->alias);
274 }
275
Rahul Sharma976face2014-03-12 20:26:44 +0530276 samsung_clk_add_lookup(ctx, clk, list->id);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900277 }
278}
279
280/*
281 * obtain the clock speed of all external fixed clock sources from device
282 * tree and register it
283 */
Sachin Kamat6cec9082013-04-08 21:35:25 +0900284#ifdef CONFIG_OF
Rahul Sharma976face2014-03-12 20:26:44 +0530285void __init samsung_clk_of_register_fixed_ext(struct samsung_clk_provider *ctx,
Thomas Abraham721c42a2013-03-09 17:02:44 +0900286 struct samsung_fixed_rate_clock *fixed_rate_clk,
287 unsigned int nr_fixed_rate_clk,
Krzysztof Kozlowski305cfab2014-06-26 14:00:06 +0200288 const struct of_device_id *clk_matches)
Thomas Abraham721c42a2013-03-09 17:02:44 +0900289{
290 const struct of_device_id *match;
Rahul Sharma976face2014-03-12 20:26:44 +0530291 struct device_node *clk_np;
Thomas Abraham721c42a2013-03-09 17:02:44 +0900292 u32 freq;
293
Rahul Sharma976face2014-03-12 20:26:44 +0530294 for_each_matching_node_and_match(clk_np, clk_matches, &match) {
295 if (of_property_read_u32(clk_np, "clock-frequency", &freq))
Thomas Abraham721c42a2013-03-09 17:02:44 +0900296 continue;
Pankaj Dubey42fb57c2014-02-26 11:42:41 +0900297 fixed_rate_clk[(unsigned long)match->data].fixed_rate = freq;
Thomas Abraham721c42a2013-03-09 17:02:44 +0900298 }
Rahul Sharma976face2014-03-12 20:26:44 +0530299 samsung_clk_register_fixed_rate(ctx, fixed_rate_clk, nr_fixed_rate_clk);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900300}
Sachin Kamat6cec9082013-04-08 21:35:25 +0900301#endif
Thomas Abraham721c42a2013-03-09 17:02:44 +0900302
303/* utility function to get the rate of a specified clock */
304unsigned long _get_rate(const char *clk_name)
305{
306 struct clk *clk;
Thomas Abraham721c42a2013-03-09 17:02:44 +0900307
Tomasz Figa3a647892013-08-26 19:09:00 +0200308 clk = __clk_lookup(clk_name);
309 if (!clk) {
Thomas Abraham721c42a2013-03-09 17:02:44 +0900310 pr_err("%s: could not find clock %s\n", __func__, clk_name);
311 return 0;
312 }
Tomasz Figa3a647892013-08-26 19:09:00 +0200313
314 return clk_get_rate(clk);
Thomas Abraham721c42a2013-03-09 17:02:44 +0900315}