]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/clk/samsung/clk.c
clk: samsung: fix pm init on non-dt platforms
[karo-tx-linux.git] / drivers / clk / samsung / clk.c
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)
58 {
59         reg_base = base;
60
61 #ifdef CONFIG_PM_SLEEP
62         if (rdump && nr_rdump) {
63                 unsigned int idx;
64                 reg_dump = kzalloc(sizeof(struct samsung_clk_reg_dump)
65                                         * nr_rdump, GFP_KERNEL);
66                 if (!reg_dump) {
67                         pr_err("%s: memory alloc for register dump failed\n",
68                                         __func__);
69                         return;
70                 }
71
72                 for (idx = 0; idx < nr_rdump; idx++)
73                         reg_dump[idx].offset = rdump[idx];
74                 nr_reg_dump = nr_rdump;
75                 register_syscore_ops(&samsung_clk_syscore_ops);
76         }
77 #endif
78
79         if (!np)
80                 return;
81
82 #ifdef CONFIG_OF
83         clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL);
84         if (!clk_table)
85                 panic("could not allocate clock lookup table\n");
86
87         clk_data.clks = clk_table;
88         clk_data.clk_num = nr_clks;
89         of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
90 #endif
91 }
92
93 /* add a clock instance to the clock lookup table used for dt based lookup */
94 void samsung_clk_add_lookup(struct clk *clk, unsigned int id)
95 {
96         if (clk_table && id)
97                 clk_table[id] = clk;
98 }
99
100 /* register a list of fixed clocks */
101 void __init samsung_clk_register_fixed_rate(
102                 struct samsung_fixed_rate_clock *list, unsigned int nr_clk)
103 {
104         struct clk *clk;
105         unsigned int idx, ret;
106
107         for (idx = 0; idx < nr_clk; idx++, list++) {
108                 clk = clk_register_fixed_rate(NULL, list->name,
109                         list->parent_name, list->flags, list->fixed_rate);
110                 if (IS_ERR(clk)) {
111                         pr_err("%s: failed to register clock %s\n", __func__,
112                                 list->name);
113                         continue;
114                 }
115
116                 samsung_clk_add_lookup(clk, list->id);
117
118                 /*
119                  * Unconditionally add a clock lookup for the fixed rate clocks.
120                  * There are not many of these on any of Samsung platforms.
121                  */
122                 ret = clk_register_clkdev(clk, list->name, NULL);
123                 if (ret)
124                         pr_err("%s: failed to register clock lookup for %s",
125                                 __func__, list->name);
126         }
127 }
128
129 /* register a list of fixed factor clocks */
130 void __init samsung_clk_register_fixed_factor(
131                 struct samsung_fixed_factor_clock *list, unsigned int nr_clk)
132 {
133         struct clk *clk;
134         unsigned int idx;
135
136         for (idx = 0; idx < nr_clk; idx++, list++) {
137                 clk = clk_register_fixed_factor(NULL, list->name,
138                         list->parent_name, list->flags, list->mult, list->div);
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(clk, list->id);
146         }
147 }
148
149 /* register a list of mux clocks */
150 void __init samsung_clk_register_mux(struct samsung_mux_clock *list,
151                                         unsigned int nr_clk)
152 {
153         struct clk *clk;
154         unsigned int idx, ret;
155
156         for (idx = 0; idx < nr_clk; idx++, list++) {
157                 clk = clk_register_mux(NULL, list->name, list->parent_names,
158                         list->num_parents, list->flags, reg_base + list->offset,
159                         list->shift, list->width, list->mux_flags, &lock);
160                 if (IS_ERR(clk)) {
161                         pr_err("%s: failed to register clock %s\n", __func__,
162                                 list->name);
163                         continue;
164                 }
165
166                 samsung_clk_add_lookup(clk, list->id);
167
168                 /* register a clock lookup only if a clock alias is specified */
169                 if (list->alias) {
170                         ret = clk_register_clkdev(clk, list->alias,
171                                                 list->dev_name);
172                         if (ret)
173                                 pr_err("%s: failed to register lookup %s\n",
174                                                 __func__, list->alias);
175                 }
176         }
177 }
178
179 /* register a list of div clocks */
180 void __init samsung_clk_register_div(struct samsung_div_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                 if (list->table)
188                         clk = clk_register_divider_table(NULL, list->name,
189                                         list->parent_name, list->flags,
190                                         reg_base + list->offset, list->shift,
191                                         list->width, list->div_flags,
192                                         list->table, &lock);
193                 else
194                         clk = clk_register_divider(NULL, list->name,
195                                         list->parent_name, list->flags,
196                                         reg_base + list->offset, list->shift,
197                                         list->width, list->div_flags, &lock);
198                 if (IS_ERR(clk)) {
199                         pr_err("%s: failed to register clock %s\n", __func__,
200                                 list->name);
201                         continue;
202                 }
203
204                 samsung_clk_add_lookup(clk, list->id);
205
206                 /* register a clock lookup only if a clock alias is specified */
207                 if (list->alias) {
208                         ret = clk_register_clkdev(clk, list->alias,
209                                                 list->dev_name);
210                         if (ret)
211                                 pr_err("%s: failed to register lookup %s\n",
212                                                 __func__, list->alias);
213                 }
214         }
215 }
216
217 /* register a list of gate clocks */
218 void __init samsung_clk_register_gate(struct samsung_gate_clock *list,
219                                                 unsigned int nr_clk)
220 {
221         struct clk *clk;
222         unsigned int idx, ret;
223
224         for (idx = 0; idx < nr_clk; idx++, list++) {
225                 clk = clk_register_gate(NULL, list->name, list->parent_name,
226                                 list->flags, reg_base + list->offset,
227                                 list->bit_idx, list->gate_flags, &lock);
228                 if (IS_ERR(clk)) {
229                         pr_err("%s: failed to register clock %s\n", __func__,
230                                 list->name);
231                         continue;
232                 }
233
234                 /* register a clock lookup only if a clock alias is specified */
235                 if (list->alias) {
236                         ret = clk_register_clkdev(clk, list->alias,
237                                                         list->dev_name);
238                         if (ret)
239                                 pr_err("%s: failed to register lookup %s\n",
240                                         __func__, list->alias);
241                 }
242
243                 samsung_clk_add_lookup(clk, list->id);
244         }
245 }
246
247 /*
248  * obtain the clock speed of all external fixed clock sources from device
249  * tree and register it
250  */
251 void __init samsung_clk_of_register_fixed_ext(
252                         struct samsung_fixed_rate_clock *fixed_rate_clk,
253                         unsigned int nr_fixed_rate_clk,
254                         struct of_device_id *clk_matches)
255 {
256         const struct of_device_id *match;
257         struct device_node *np;
258         u32 freq;
259
260         for_each_matching_node_and_match(np, clk_matches, &match) {
261                 if (of_property_read_u32(np, "clock-frequency", &freq))
262                         continue;
263                 fixed_rate_clk[(u32)match->data].fixed_rate = freq;
264         }
265         samsung_clk_register_fixed_rate(fixed_rate_clk, nr_fixed_rate_clk);
266 }
267
268 /* utility function to get the rate of a specified clock */
269 unsigned long _get_rate(const char *clk_name)
270 {
271         struct clk *clk;
272         unsigned long rate;
273
274         clk = clk_get(NULL, clk_name);
275         if (IS_ERR(clk)) {
276                 pr_err("%s: could not find clock %s\n", __func__, clk_name);
277                 return 0;
278         }
279         rate = clk_get_rate(clk);
280         clk_put(clk);
281         return rate;
282 }