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