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