xref: /openbmc/linux/drivers/clk/samsung/clk.c (revision cd5d5810)
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 static DEFINE_SPINLOCK(lock);
18 static struct clk **clk_table;
19 static void __iomem *reg_base;
20 #ifdef CONFIG_OF
21 static struct clk_onecell_data clk_data;
22 #endif
23 
24 #ifdef CONFIG_PM_SLEEP
25 static struct samsung_clk_reg_dump *reg_dump;
26 static unsigned long nr_reg_dump;
27 
28 static int samsung_clk_suspend(void)
29 {
30 	struct samsung_clk_reg_dump *rd = reg_dump;
31 	unsigned long i;
32 
33 	for (i = 0; i < nr_reg_dump; i++, rd++)
34 		rd->value = __raw_readl(reg_base + rd->offset);
35 
36 	return 0;
37 }
38 
39 static void samsung_clk_resume(void)
40 {
41 	struct samsung_clk_reg_dump *rd = reg_dump;
42 	unsigned long i;
43 
44 	for (i = 0; i < nr_reg_dump; i++, rd++)
45 		__raw_writel(rd->value, reg_base + rd->offset);
46 }
47 
48 static struct syscore_ops samsung_clk_syscore_ops = {
49 	.suspend	= samsung_clk_suspend,
50 	.resume		= samsung_clk_resume,
51 };
52 #endif /* CONFIG_PM_SLEEP */
53 
54 /* setup the essentials required to support clock lookup using ccf */
55 void __init samsung_clk_init(struct device_node *np, void __iomem *base,
56 		unsigned long nr_clks, unsigned long *rdump,
57 		unsigned long nr_rdump, unsigned long *soc_rdump,
58 		unsigned long nr_soc_rdump)
59 {
60 	reg_base = base;
61 
62 #ifdef CONFIG_PM_SLEEP
63 	if (rdump && nr_rdump) {
64 		unsigned int idx;
65 		reg_dump = kzalloc(sizeof(struct samsung_clk_reg_dump)
66 				* (nr_rdump + nr_soc_rdump), GFP_KERNEL);
67 		if (!reg_dump) {
68 			pr_err("%s: memory alloc for register dump failed\n",
69 					__func__);
70 			return;
71 		}
72 
73 		for (idx = 0; idx < nr_rdump; idx++)
74 			reg_dump[idx].offset = rdump[idx];
75 		for (idx = 0; idx < nr_soc_rdump; idx++)
76 			reg_dump[nr_rdump + idx].offset = soc_rdump[idx];
77 		nr_reg_dump = nr_rdump + nr_soc_rdump;
78 		register_syscore_ops(&samsung_clk_syscore_ops);
79 	}
80 #endif
81 
82 	clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL);
83 	if (!clk_table)
84 		panic("could not allocate clock lookup table\n");
85 
86 	if (!np)
87 		return;
88 
89 #ifdef CONFIG_OF
90 	clk_data.clks = clk_table;
91 	clk_data.clk_num = nr_clks;
92 	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
93 #endif
94 }
95 
96 /* add a clock instance to the clock lookup table used for dt based lookup */
97 void samsung_clk_add_lookup(struct clk *clk, unsigned int id)
98 {
99 	if (clk_table && id)
100 		clk_table[id] = clk;
101 }
102 
103 /* register a list of aliases */
104 void __init samsung_clk_register_alias(struct samsung_clock_alias *list,
105 					unsigned int nr_clk)
106 {
107 	struct clk *clk;
108 	unsigned int idx, ret;
109 
110 	if (!clk_table) {
111 		pr_err("%s: clock table missing\n", __func__);
112 		return;
113 	}
114 
115 	for (idx = 0; idx < nr_clk; idx++, list++) {
116 		if (!list->id) {
117 			pr_err("%s: clock id missing for index %d\n", __func__,
118 				idx);
119 			continue;
120 		}
121 
122 		clk = clk_table[list->id];
123 		if (!clk) {
124 			pr_err("%s: failed to find clock %d\n", __func__,
125 				list->id);
126 			continue;
127 		}
128 
129 		ret = clk_register_clkdev(clk, list->alias, list->dev_name);
130 		if (ret)
131 			pr_err("%s: failed to register lookup %s\n",
132 					__func__, list->alias);
133 	}
134 }
135 
136 /* register a list of fixed clocks */
137 void __init samsung_clk_register_fixed_rate(
138 		struct samsung_fixed_rate_clock *list, unsigned int nr_clk)
139 {
140 	struct clk *clk;
141 	unsigned int idx, ret;
142 
143 	for (idx = 0; idx < nr_clk; idx++, list++) {
144 		clk = clk_register_fixed_rate(NULL, list->name,
145 			list->parent_name, list->flags, list->fixed_rate);
146 		if (IS_ERR(clk)) {
147 			pr_err("%s: failed to register clock %s\n", __func__,
148 				list->name);
149 			continue;
150 		}
151 
152 		samsung_clk_add_lookup(clk, list->id);
153 
154 		/*
155 		 * Unconditionally add a clock lookup for the fixed rate clocks.
156 		 * There are not many of these on any of Samsung platforms.
157 		 */
158 		ret = clk_register_clkdev(clk, list->name, NULL);
159 		if (ret)
160 			pr_err("%s: failed to register clock lookup for %s",
161 				__func__, list->name);
162 	}
163 }
164 
165 /* register a list of fixed factor clocks */
166 void __init samsung_clk_register_fixed_factor(
167 		struct samsung_fixed_factor_clock *list, unsigned int nr_clk)
168 {
169 	struct clk *clk;
170 	unsigned int idx;
171 
172 	for (idx = 0; idx < nr_clk; idx++, list++) {
173 		clk = clk_register_fixed_factor(NULL, list->name,
174 			list->parent_name, list->flags, list->mult, list->div);
175 		if (IS_ERR(clk)) {
176 			pr_err("%s: failed to register clock %s\n", __func__,
177 				list->name);
178 			continue;
179 		}
180 
181 		samsung_clk_add_lookup(clk, list->id);
182 	}
183 }
184 
185 /* register a list of mux clocks */
186 void __init samsung_clk_register_mux(struct samsung_mux_clock *list,
187 					unsigned int nr_clk)
188 {
189 	struct clk *clk;
190 	unsigned int idx, ret;
191 
192 	for (idx = 0; idx < nr_clk; idx++, list++) {
193 		clk = clk_register_mux(NULL, list->name, list->parent_names,
194 			list->num_parents, list->flags, reg_base + list->offset,
195 			list->shift, list->width, list->mux_flags, &lock);
196 		if (IS_ERR(clk)) {
197 			pr_err("%s: failed to register clock %s\n", __func__,
198 				list->name);
199 			continue;
200 		}
201 
202 		samsung_clk_add_lookup(clk, list->id);
203 
204 		/* register a clock lookup only if a clock alias is specified */
205 		if (list->alias) {
206 			ret = clk_register_clkdev(clk, list->alias,
207 						list->dev_name);
208 			if (ret)
209 				pr_err("%s: failed to register lookup %s\n",
210 						__func__, list->alias);
211 		}
212 	}
213 }
214 
215 /* register a list of div clocks */
216 void __init samsung_clk_register_div(struct samsung_div_clock *list,
217 					unsigned int nr_clk)
218 {
219 	struct clk *clk;
220 	unsigned int idx, ret;
221 
222 	for (idx = 0; idx < nr_clk; idx++, list++) {
223 		if (list->table)
224 			clk = clk_register_divider_table(NULL, list->name,
225 					list->parent_name, list->flags,
226 					reg_base + list->offset, list->shift,
227 					list->width, list->div_flags,
228 					list->table, &lock);
229 		else
230 			clk = clk_register_divider(NULL, list->name,
231 					list->parent_name, list->flags,
232 					reg_base + list->offset, list->shift,
233 					list->width, list->div_flags, &lock);
234 		if (IS_ERR(clk)) {
235 			pr_err("%s: failed to register clock %s\n", __func__,
236 				list->name);
237 			continue;
238 		}
239 
240 		samsung_clk_add_lookup(clk, list->id);
241 
242 		/* register a clock lookup only if a clock alias is specified */
243 		if (list->alias) {
244 			ret = clk_register_clkdev(clk, list->alias,
245 						list->dev_name);
246 			if (ret)
247 				pr_err("%s: failed to register lookup %s\n",
248 						__func__, list->alias);
249 		}
250 	}
251 }
252 
253 /* register a list of gate clocks */
254 void __init samsung_clk_register_gate(struct samsung_gate_clock *list,
255 						unsigned int nr_clk)
256 {
257 	struct clk *clk;
258 	unsigned int idx, ret;
259 
260 	for (idx = 0; idx < nr_clk; idx++, list++) {
261 		clk = clk_register_gate(NULL, list->name, list->parent_name,
262 				list->flags, reg_base + list->offset,
263 				list->bit_idx, list->gate_flags, &lock);
264 		if (IS_ERR(clk)) {
265 			pr_err("%s: failed to register clock %s\n", __func__,
266 				list->name);
267 			continue;
268 		}
269 
270 		/* register a clock lookup only if a clock alias is specified */
271 		if (list->alias) {
272 			ret = clk_register_clkdev(clk, list->alias,
273 							list->dev_name);
274 			if (ret)
275 				pr_err("%s: failed to register lookup %s\n",
276 					__func__, list->alias);
277 		}
278 
279 		samsung_clk_add_lookup(clk, list->id);
280 	}
281 }
282 
283 /*
284  * obtain the clock speed of all external fixed clock sources from device
285  * tree and register it
286  */
287 #ifdef CONFIG_OF
288 void __init samsung_clk_of_register_fixed_ext(
289 			struct samsung_fixed_rate_clock *fixed_rate_clk,
290 			unsigned int nr_fixed_rate_clk,
291 			struct of_device_id *clk_matches)
292 {
293 	const struct of_device_id *match;
294 	struct device_node *np;
295 	u32 freq;
296 
297 	for_each_matching_node_and_match(np, clk_matches, &match) {
298 		if (of_property_read_u32(np, "clock-frequency", &freq))
299 			continue;
300 		fixed_rate_clk[(u32)match->data].fixed_rate = freq;
301 	}
302 	samsung_clk_register_fixed_rate(fixed_rate_clk, nr_fixed_rate_clk);
303 }
304 #endif
305 
306 /* utility function to get the rate of a specified clock */
307 unsigned long _get_rate(const char *clk_name)
308 {
309 	struct clk *clk;
310 
311 	clk = __clk_lookup(clk_name);
312 	if (!clk) {
313 		pr_err("%s: could not find clock %s\n", __func__, clk_name);
314 		return 0;
315 	}
316 
317 	return clk_get_rate(clk);
318 }
319