xref: /openbmc/linux/drivers/clk/samsung/clk.c (revision d2999e1b)
1 /*
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 
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 
33 struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump(
34 						const unsigned long *rdump,
35 						unsigned long nr_rdump)
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 
50 /* setup the essentials required to support clock lookup using ccf */
51 struct samsung_clk_provider *__init samsung_clk_init(struct device_node *np,
52 			void __iomem *base, unsigned long nr_clks)
53 {
54 	struct samsung_clk_provider *ctx;
55 	struct clk **clk_table;
56 	int ret;
57 	int i;
58 
59 	ctx = kzalloc(sizeof(struct samsung_clk_provider), GFP_KERNEL);
60 	if (!ctx)
61 		panic("could not allocate clock provider context.\n");
62 
63 	clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL);
64 	if (!clk_table)
65 		panic("could not allocate clock lookup table\n");
66 
67 	for (i = 0; i < nr_clks; ++i)
68 		clk_table[i] = ERR_PTR(-ENOENT);
69 
70 	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);
74 
75 	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;
84 }
85 
86 /* add a clock instance to the clock lookup table used for dt based lookup */
87 void samsung_clk_add_lookup(struct samsung_clk_provider *ctx, struct clk *clk,
88 				unsigned int id)
89 {
90 	if (ctx->clk_data.clks && id)
91 		ctx->clk_data.clks[id] = clk;
92 }
93 
94 /* register a list of aliases */
95 void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx,
96 				struct samsung_clock_alias *list,
97 				unsigned int nr_clk)
98 {
99 	struct clk *clk;
100 	unsigned int idx, ret;
101 
102 	if (!ctx->clk_data.clks) {
103 		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 
114 		clk = ctx->clk_data.clks[list->id];
115 		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 
128 /* register a list of fixed clocks */
129 void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx,
130 		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 
144 		samsung_clk_add_lookup(ctx, clk, list->id);
145 
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 */
158 void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx,
159 		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 
173 		samsung_clk_add_lookup(ctx, clk, list->id);
174 	}
175 }
176 
177 /* register a list of mux clocks */
178 void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx,
179 				struct samsung_mux_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++) {
186 		clk = clk_register_mux(NULL, list->name, list->parent_names,
187 			list->num_parents, list->flags,
188 			ctx->reg_base + list->offset,
189 			list->shift, list->width, list->mux_flags, &ctx->lock);
190 		if (IS_ERR(clk)) {
191 			pr_err("%s: failed to register clock %s\n", __func__,
192 				list->name);
193 			continue;
194 		}
195 
196 		samsung_clk_add_lookup(ctx, clk, list->id);
197 
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 */
210 void __init samsung_clk_register_div(struct samsung_clk_provider *ctx,
211 				struct samsung_div_clock *list,
212 				unsigned int nr_clk)
213 {
214 	struct clk *clk;
215 	unsigned int idx, ret;
216 
217 	for (idx = 0; idx < nr_clk; idx++, list++) {
218 		if (list->table)
219 			clk = clk_register_divider_table(NULL, list->name,
220 				list->parent_name, list->flags,
221 				ctx->reg_base + list->offset,
222 				list->shift, list->width, list->div_flags,
223 				list->table, &ctx->lock);
224 		else
225 			clk = clk_register_divider(NULL, list->name,
226 				list->parent_name, list->flags,
227 				ctx->reg_base + list->offset, list->shift,
228 				list->width, list->div_flags, &ctx->lock);
229 		if (IS_ERR(clk)) {
230 			pr_err("%s: failed to register clock %s\n", __func__,
231 				list->name);
232 			continue;
233 		}
234 
235 		samsung_clk_add_lookup(ctx, clk, list->id);
236 
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 */
249 void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx,
250 				struct samsung_gate_clock *list,
251 				unsigned int nr_clk)
252 {
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,
258 				list->flags, ctx->reg_base + list->offset,
259 				list->bit_idx, list->gate_flags, &ctx->lock);
260 		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 
275 		samsung_clk_add_lookup(ctx, clk, list->id);
276 	}
277 }
278 
279 /*
280  * obtain the clock speed of all external fixed clock sources from device
281  * tree and register it
282  */
283 #ifdef CONFIG_OF
284 void __init samsung_clk_of_register_fixed_ext(struct samsung_clk_provider *ctx,
285 			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;
290 	struct device_node *clk_np;
291 	u32 freq;
292 
293 	for_each_matching_node_and_match(clk_np, clk_matches, &match) {
294 		if (of_property_read_u32(clk_np, "clock-frequency", &freq))
295 			continue;
296 		fixed_rate_clk[(unsigned long)match->data].fixed_rate = freq;
297 	}
298 	samsung_clk_register_fixed_rate(ctx, fixed_rate_clk, nr_fixed_rate_clk);
299 }
300 #endif
301 
302 /* utility function to get the rate of a specified clock */
303 unsigned long _get_rate(const char *clk_name)
304 {
305 	struct clk *clk;
306 
307 	clk = __clk_lookup(clk_name);
308 	if (!clk) {
309 		pr_err("%s: could not find clock %s\n", __func__, clk_name);
310 		return 0;
311 	}
312 
313 	return clk_get_rate(clk);
314 }
315