aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk/samsung
diff options
context:
space:
mode:
authorThomas Abraham <thomas.abraham@linaro.org>2013-03-09 03:02:44 -0500
committerKukjin Kim <kgene.kim@samsung.com>2013-03-25 05:16:09 -0400
commit721c42a351b113a9633d2447d9131fe620f2d805 (patch)
tree9e52f223570c56cb9c5e775670bbc2e2f0fd03f8 /drivers/clk/samsung
parent8ec46b97f24d60645c8f708c87e0caecebd25d77 (diff)
clk: samsung: add common clock framework helper functions for Samsung platforms
All Samsung platforms include different types of clock including fixed-rate, mux, divider and gate clock types. There are typically hundreds of such clocks on each of the Samsung platforms. To enable Samsung platforms to register these clocks using the common clock framework, a bunch of utility functions are introduced here which simplify the clock registration process. The clocks are usually statically instantiated and registered with common clock framework. Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Tested-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Reviewed-by: Tomasz Figa <t.figa@samsung.com> Tested-by: Tomasz Figa <t.figa@samsung.com> Tested-by: Heiko Stuebner <heiko@sntech.de> Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org> Acked-by: Mike Turquette <mturquette@linaro.org> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
Diffstat (limited to 'drivers/clk/samsung')
-rw-r--r--drivers/clk/samsung/Makefile5
-rw-r--r--drivers/clk/samsung/clk.c273
-rw-r--r--drivers/clk/samsung/clk.h262
3 files changed, 540 insertions, 0 deletions
diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile
new file mode 100644
index 000000000000..bd920b43f6e7
--- /dev/null
+++ b/drivers/clk/samsung/Makefile
@@ -0,0 +1,5 @@
1#
2# Samsung Clock specific Makefile
3#
4
5obj-$(CONFIG_COMMON_CLK) += clk.o
diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c
new file mode 100644
index 000000000000..91d12f397f5d
--- /dev/null
+++ b/drivers/clk/samsung/clk.c
@@ -0,0 +1,273 @@
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
17static DEFINE_SPINLOCK(lock);
18static struct clk **clk_table;
19static void __iomem *reg_base;
20#ifdef CONFIG_OF
21static struct clk_onecell_data clk_data;
22#endif
23
24#ifdef CONFIG_PM_SLEEP
25static struct samsung_clk_reg_dump *reg_dump;
26static unsigned long nr_reg_dump;
27
28static 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
39static 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
48static 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 */
55void __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 if (!np)
61 return;
62
63#ifdef CONFIG_OF
64 clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL);
65 if (!clk_table)
66 panic("could not allocate clock lookup table\n");
67
68 clk_data.clks = clk_table;
69 clk_data.clk_num = nr_clks;
70 of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
71#endif
72
73#ifdef CONFIG_PM_SLEEP
74 if (rdump && nr_rdump) {
75 unsigned int idx;
76 reg_dump = kzalloc(sizeof(struct samsung_clk_reg_dump)
77 * nr_rdump, GFP_KERNEL);
78 if (!reg_dump) {
79 pr_err("%s: memory alloc for register dump failed\n",
80 __func__);
81 return;
82 }
83
84 for (idx = 0; idx < nr_rdump; idx++)
85 reg_dump[idx].offset = rdump[idx];
86 nr_reg_dump = nr_rdump;
87 register_syscore_ops(&samsung_clk_syscore_ops);
88 }
89#endif
90}
91
92/* add a clock instance to the clock lookup table used for dt based lookup */
93void samsung_clk_add_lookup(struct clk *clk, unsigned int id)
94{
95 if (clk_table && id)
96 clk_table[id] = clk;
97}
98
99/* register a list of fixed clocks */
100void __init samsung_clk_register_fixed_rate(
101 struct samsung_fixed_rate_clock *list, unsigned int nr_clk)
102{
103 struct clk *clk;
104 unsigned int idx, ret;
105
106 for (idx = 0; idx < nr_clk; idx++, list++) {
107 clk = clk_register_fixed_rate(NULL, list->name,
108 list->parent_name, list->flags, list->fixed_rate);
109 if (IS_ERR(clk)) {
110 pr_err("%s: failed to register clock %s\n", __func__,
111 list->name);
112 continue;
113 }
114
115 samsung_clk_add_lookup(clk, list->id);
116
117 /*
118 * Unconditionally add a clock lookup for the fixed rate clocks.
119 * There are not many of these on any of Samsung platforms.
120 */
121 ret = clk_register_clkdev(clk, list->name, NULL);
122 if (ret)
123 pr_err("%s: failed to register clock lookup for %s",
124 __func__, list->name);
125 }
126}
127
128/* register a list of fixed factor clocks */
129void __init samsung_clk_register_fixed_factor(
130 struct samsung_fixed_factor_clock *list, unsigned int nr_clk)
131{
132 struct clk *clk;
133 unsigned int idx;
134
135 for (idx = 0; idx < nr_clk; idx++, list++) {
136 clk = clk_register_fixed_factor(NULL, list->name,
137 list->parent_name, list->flags, list->mult, list->div);
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(clk, list->id);
145 }
146}
147
148/* register a list of mux clocks */
149void __init samsung_clk_register_mux(struct samsung_mux_clock *list,
150 unsigned int nr_clk)
151{
152 struct clk *clk;
153 unsigned int idx, ret;
154
155 for (idx = 0; idx < nr_clk; idx++, list++) {
156 clk = clk_register_mux(NULL, list->name, list->parent_names,
157 list->num_parents, list->flags, reg_base + list->offset,
158 list->shift, list->width, list->mux_flags, &lock);
159 if (IS_ERR(clk)) {
160 pr_err("%s: failed to register clock %s\n", __func__,
161 list->name);
162 continue;
163 }
164
165 samsung_clk_add_lookup(clk, list->id);
166
167 /* register a clock lookup only if a clock alias is specified */
168 if (list->alias) {
169 ret = clk_register_clkdev(clk, list->alias,
170 list->dev_name);
171 if (ret)
172 pr_err("%s: failed to register lookup %s\n",
173 __func__, list->alias);
174 }
175 }
176}
177
178/* register a list of div clocks */
179void __init samsung_clk_register_div(struct samsung_div_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_divider(NULL, list->name, list->parent_name,
187 list->flags, reg_base + list->offset, list->shift,
188 list->width, list->div_flags, &lock);
189 if (IS_ERR(clk)) {
190 pr_err("%s: failed to register clock %s\n", __func__,
191 list->name);
192 continue;
193 }
194
195 samsung_clk_add_lookup(clk, list->id);
196
197 /* register a clock lookup only if a clock alias is specified */
198 if (list->alias) {
199 ret = clk_register_clkdev(clk, list->alias,
200 list->dev_name);
201 if (ret)
202 pr_err("%s: failed to register lookup %s\n",
203 __func__, list->alias);
204 }
205 }
206}
207
208/* register a list of gate clocks */
209void __init samsung_clk_register_gate(struct samsung_gate_clock *list,
210 unsigned int nr_clk)
211{
212 struct clk *clk;
213 unsigned int idx, ret;
214
215 for (idx = 0; idx < nr_clk; idx++, list++) {
216 clk = clk_register_gate(NULL, list->name, list->parent_name,
217 list->flags, reg_base + list->offset,
218 list->bit_idx, list->gate_flags, &lock);
219 if (IS_ERR(clk)) {
220 pr_err("%s: failed to register clock %s\n", __func__,
221 list->name);
222 continue;
223 }
224
225 /* register a clock lookup only if a clock alias is specified */
226 if (list->alias) {
227 ret = clk_register_clkdev(clk, list->alias,
228 list->dev_name);
229 if (ret)
230 pr_err("%s: failed to register lookup %s\n",
231 __func__, list->alias);
232 }
233
234 samsung_clk_add_lookup(clk, list->id);
235 }
236}
237
238/*
239 * obtain the clock speed of all external fixed clock sources from device
240 * tree and register it
241 */
242void __init samsung_clk_of_register_fixed_ext(
243 struct samsung_fixed_rate_clock *fixed_rate_clk,
244 unsigned int nr_fixed_rate_clk,
245 struct of_device_id *clk_matches)
246{
247 const struct of_device_id *match;
248 struct device_node *np;
249 u32 freq;
250
251 for_each_matching_node_and_match(np, clk_matches, &match) {
252 if (of_property_read_u32(np, "clock-frequency", &freq))
253 continue;
254 fixed_rate_clk[(u32)match->data].fixed_rate = freq;
255 }
256 samsung_clk_register_fixed_rate(fixed_rate_clk, nr_fixed_rate_clk);
257}
258
259/* utility function to get the rate of a specified clock */
260unsigned long _get_rate(const char *clk_name)
261{
262 struct clk *clk;
263 unsigned long rate;
264
265 clk = clk_get(NULL, clk_name);
266 if (IS_ERR(clk)) {
267 pr_err("%s: could not find clock %s\n", __func__, clk_name);
268 return 0;
269 }
270 rate = clk_get_rate(clk);
271 clk_put(clk);
272 return rate;
273}
diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
new file mode 100644
index 000000000000..961192ffd696
--- /dev/null
+++ b/drivers/clk/samsung/clk.h
@@ -0,0 +1,262 @@
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 * Common Clock Framework support for all Samsung platforms
11*/
12
13#ifndef __SAMSUNG_CLK_H
14#define __SAMSUNG_CLK_H
15
16#include <linux/clk.h>
17#include <linux/clkdev.h>
18#include <linux/io.h>
19#include <linux/clk-provider.h>
20#include <linux/of.h>
21#include <linux/of_address.h>
22
23#include <mach/map.h>
24
25/**
26 * struct samsung_fixed_rate_clock: information about fixed-rate clock
27 * @id: platform specific id of the clock.
28 * @name: name of this fixed-rate clock.
29 * @parent_name: optional parent clock name.
30 * @flags: optional fixed-rate clock flags.
31 * @fixed-rate: fixed clock rate of this clock.
32 */
33struct samsung_fixed_rate_clock {
34 unsigned int id;
35 char *name;
36 const char *parent_name;
37 unsigned long flags;
38 unsigned long fixed_rate;
39};
40
41#define FRATE(_id, cname, pname, f, frate) \
42 { \
43 .id = _id, \
44 .name = cname, \
45 .parent_name = pname, \
46 .flags = f, \
47 .fixed_rate = frate, \
48 }
49
50/*
51 * struct samsung_fixed_factor_clock: information about fixed-factor clock
52 * @id: platform specific id of the clock.
53 * @name: name of this fixed-factor clock.
54 * @parent_name: parent clock name.
55 * @mult: fixed multiplication factor.
56 * @div: fixed division factor.
57 * @flags: optional fixed-factor clock flags.
58 */
59struct samsung_fixed_factor_clock {
60 unsigned int id;
61 char *name;
62 const char *parent_name;
63 unsigned long mult;
64 unsigned long div;
65 unsigned long flags;
66};
67
68#define FFACTOR(_id, cname, pname, m, d, f) \
69 { \
70 .id = _id, \
71 .name = cname, \
72 .parent_name = pname, \
73 .mult = m, \
74 .div = d, \
75 .flags = f, \
76 }
77
78/**
79 * struct samsung_mux_clock: information about mux clock
80 * @id: platform specific id of the clock.
81 * @dev_name: name of the device to which this clock belongs.
82 * @name: name of this mux clock.
83 * @parent_names: array of pointer to parent clock names.
84 * @num_parents: number of parents listed in @parent_names.
85 * @flags: optional flags for basic clock.
86 * @offset: offset of the register for configuring the mux.
87 * @shift: starting bit location of the mux control bit-field in @reg.
88 * @width: width of the mux control bit-field in @reg.
89 * @mux_flags: flags for mux-type clock.
90 * @alias: optional clock alias name to be assigned to this clock.
91 */
92struct samsung_mux_clock {
93 unsigned int id;
94 const char *dev_name;
95 const char *name;
96 const char **parent_names;
97 u8 num_parents;
98 unsigned long flags;
99 unsigned long offset;
100 u8 shift;
101 u8 width;
102 u8 mux_flags;
103 const char *alias;
104};
105
106#define __MUX(_id, dname, cname, pnames, o, s, w, f, mf, a) \
107 { \
108 .id = _id, \
109 .dev_name = dname, \
110 .name = cname, \
111 .parent_names = pnames, \
112 .num_parents = ARRAY_SIZE(pnames), \
113 .flags = f, \
114 .offset = o, \
115 .shift = s, \
116 .width = w, \
117 .mux_flags = mf, \
118 .alias = a, \
119 }
120
121#define MUX(_id, cname, pnames, o, s, w) \
122 __MUX(_id, NULL, cname, pnames, o, s, w, 0, 0, NULL)
123
124#define MUX_A(_id, cname, pnames, o, s, w, a) \
125 __MUX(_id, NULL, cname, pnames, o, s, w, 0, 0, a)
126
127#define MUX_F(_id, cname, pnames, o, s, w, f, mf) \
128 __MUX(_id, NULL, cname, pnames, o, s, w, f, mf, NULL)
129
130/**
131 * @id: platform specific id of the clock.
132 * struct samsung_div_clock: information about div clock
133 * @dev_name: name of the device to which this clock belongs.
134 * @name: name of this div clock.
135 * @parent_name: name of the parent clock.
136 * @flags: optional flags for basic clock.
137 * @offset: offset of the register for configuring the div.
138 * @shift: starting bit location of the div control bit-field in @reg.
139 * @div_flags: flags for div-type clock.
140 * @alias: optional clock alias name to be assigned to this clock.
141 */
142struct samsung_div_clock {
143 unsigned int id;
144 const char *dev_name;
145 const char *name;
146 const char *parent_name;
147 unsigned long flags;
148 unsigned long offset;
149 u8 shift;
150 u8 width;
151 u8 div_flags;
152 const char *alias;
153};
154
155#define __DIV(_id, dname, cname, pname, o, s, w, f, df, a) \
156 { \
157 .id = _id, \
158 .dev_name = dname, \
159 .name = cname, \
160 .parent_name = pname, \
161 .flags = f, \
162 .offset = o, \
163 .shift = s, \
164 .width = w, \
165 .div_flags = df, \
166 .alias = a, \
167 }
168
169#define DIV(_id, cname, pname, o, s, w) \
170 __DIV(_id, NULL, cname, pname, o, s, w, 0, 0, NULL)
171
172#define DIV_A(_id, cname, pname, o, s, w, a) \
173 __DIV(_id, NULL, cname, pname, o, s, w, 0, 0, a)
174
175#define DIV_F(_id, cname, pname, o, s, w, f, df) \
176 __DIV(_id, NULL, cname, pname, o, s, w, f, df, NULL)
177
178/**
179 * struct samsung_gate_clock: information about gate clock
180 * @id: platform specific id of the clock.
181 * @dev_name: name of the device to which this clock belongs.
182 * @name: name of this gate clock.
183 * @parent_name: name of the parent clock.
184 * @flags: optional flags for basic clock.
185 * @offset: offset of the register for configuring the gate.
186 * @bit_idx: bit index of the gate control bit-field in @reg.
187 * @gate_flags: flags for gate-type clock.
188 * @alias: optional clock alias name to be assigned to this clock.
189 */
190struct samsung_gate_clock {
191 unsigned int id;
192 const char *dev_name;
193 const char *name;
194 const char *parent_name;
195 unsigned long flags;
196 unsigned long offset;
197 u8 bit_idx;
198 u8 gate_flags;
199 const char *alias;
200};
201
202#define __GATE(_id, dname, cname, pname, o, b, f, gf, a) \
203 { \
204 .id = _id, \
205 .dev_name = dname, \
206 .name = cname, \
207 .parent_name = pname, \
208 .flags = f, \
209 .offset = o, \
210 .bit_idx = b, \
211 .gate_flags = gf, \
212 .alias = a, \
213 }
214
215#define GATE(_id, cname, pname, o, b, f, gf) \
216 __GATE(_id, NULL, cname, pname, o, b, f, gf, NULL)
217
218#define GATE_A(_id, cname, pname, o, b, f, gf, a) \
219 __GATE(_id, NULL, cname, pname, o, b, f, gf, a)
220
221#define GATE_D(_id, dname, cname, pname, o, b, f, gf) \
222 __GATE(_id, dname, cname, pname, o, b, f, gf, NULL)
223
224#define GATE_DA(_id, dname, cname, pname, o, b, f, gf, a) \
225 __GATE(_id, dname, cname, pname, o, b, f, gf, a)
226
227#define PNAME(x) static const char *x[] __initdata
228
229/**
230 * struct samsung_clk_reg_dump: register dump of clock controller registers.
231 * @offset: clock register offset from the controller base address.
232 * @value: the value to be register at offset.
233 */
234struct samsung_clk_reg_dump {
235 u32 offset;
236 u32 value;
237};
238
239extern void __init samsung_clk_init(struct device_node *np, void __iomem *base,
240 unsigned long nr_clks, unsigned long *rdump,
241 unsigned long nr_rdump);
242extern void __init samsung_clk_of_register_fixed_ext(
243 struct samsung_fixed_rate_clock *fixed_rate_clk,
244 unsigned int nr_fixed_rate_clk,
245 struct of_device_id *clk_matches);
246
247extern void samsung_clk_add_lookup(struct clk *clk, unsigned int id);
248
249extern void __init samsung_clk_register_fixed_rate(
250 struct samsung_fixed_rate_clock *clk_list, unsigned int nr_clk);
251extern void __init samsung_clk_register_fixed_factor(
252 struct samsung_fixed_factor_clock *list, unsigned int nr_clk);
253extern void __init samsung_clk_register_mux(struct samsung_mux_clock *clk_list,
254 unsigned int nr_clk);
255extern void __init samsung_clk_register_div(struct samsung_div_clock *clk_list,
256 unsigned int nr_clk);
257extern void __init samsung_clk_register_gate(
258 struct samsung_gate_clock *clk_list, unsigned int nr_clk);
259
260extern unsigned long _get_rate(const char *clk_name);
261
262#endif /* __SAMSUNG_CLK_H */