diff options
Diffstat (limited to 'arch/arm/mach-bcmring/clock.c')
-rw-r--r-- | arch/arm/mach-bcmring/clock.c | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/arch/arm/mach-bcmring/clock.c b/arch/arm/mach-bcmring/clock.c new file mode 100644 index 000000000000..14bafc38f2dc --- /dev/null +++ b/arch/arm/mach-bcmring/clock.c | |||
@@ -0,0 +1,224 @@ | |||
1 | /***************************************************************************** | ||
2 | * Copyright 2001 - 2009 Broadcom Corporation. All rights reserved. | ||
3 | * | ||
4 | * Unless you and Broadcom execute a separate written software license | ||
5 | * agreement governing use of this software, this software is licensed to you | ||
6 | * under the terms of the GNU General Public License version 2, available at | ||
7 | * http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). | ||
8 | * | ||
9 | * Notwithstanding the above, under no circumstances may you combine this | ||
10 | * software in any way with any other Broadcom software provided under a | ||
11 | * license other than the GPL, without Broadcom's express prior written | ||
12 | * consent. | ||
13 | *****************************************************************************/ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/device.h> | ||
18 | #include <linux/list.h> | ||
19 | #include <linux/errno.h> | ||
20 | #include <linux/err.h> | ||
21 | #include <linux/string.h> | ||
22 | #include <linux/clk.h> | ||
23 | #include <linux/spinlock.h> | ||
24 | #include <mach/csp/hw_cfg.h> | ||
25 | #include <mach/csp/chipcHw_def.h> | ||
26 | #include <mach/csp/chipcHw_reg.h> | ||
27 | #include <mach/csp/chipcHw_inline.h> | ||
28 | |||
29 | #include <asm/clkdev.h> | ||
30 | |||
31 | #include "clock.h" | ||
32 | |||
33 | #define clk_is_primary(x) ((x)->type & CLK_TYPE_PRIMARY) | ||
34 | #define clk_is_pll1(x) ((x)->type & CLK_TYPE_PLL1) | ||
35 | #define clk_is_pll2(x) ((x)->type & CLK_TYPE_PLL2) | ||
36 | #define clk_is_programmable(x) ((x)->type & CLK_TYPE_PROGRAMMABLE) | ||
37 | #define clk_is_bypassable(x) ((x)->type & CLK_TYPE_BYPASSABLE) | ||
38 | |||
39 | #define clk_is_using_xtal(x) ((x)->mode & CLK_MODE_XTAL) | ||
40 | |||
41 | static DEFINE_SPINLOCK(clk_lock); | ||
42 | |||
43 | static void __clk_enable(struct clk *clk) | ||
44 | { | ||
45 | if (!clk) | ||
46 | return; | ||
47 | |||
48 | /* enable parent clock first */ | ||
49 | if (clk->parent) | ||
50 | __clk_enable(clk->parent); | ||
51 | |||
52 | if (clk->use_cnt++ == 0) { | ||
53 | if (clk_is_pll1(clk)) { /* PLL1 */ | ||
54 | chipcHw_pll1Enable(clk->rate_hz, 0); | ||
55 | } else if (clk_is_pll2(clk)) { /* PLL2 */ | ||
56 | chipcHw_pll2Enable(clk->rate_hz); | ||
57 | } else if (clk_is_using_xtal(clk)) { /* source is crystal */ | ||
58 | if (!clk_is_primary(clk)) | ||
59 | chipcHw_bypassClockEnable(clk->csp_id); | ||
60 | } else { /* source is PLL */ | ||
61 | chipcHw_setClockEnable(clk->csp_id); | ||
62 | } | ||
63 | } | ||
64 | } | ||
65 | |||
66 | int clk_enable(struct clk *clk) | ||
67 | { | ||
68 | unsigned long flags; | ||
69 | |||
70 | if (!clk) | ||
71 | return -EINVAL; | ||
72 | |||
73 | spin_lock_irqsave(&clk_lock, flags); | ||
74 | __clk_enable(clk); | ||
75 | spin_unlock_irqrestore(&clk_lock, flags); | ||
76 | |||
77 | return 0; | ||
78 | } | ||
79 | EXPORT_SYMBOL(clk_enable); | ||
80 | |||
81 | static void __clk_disable(struct clk *clk) | ||
82 | { | ||
83 | if (!clk) | ||
84 | return; | ||
85 | |||
86 | BUG_ON(clk->use_cnt == 0); | ||
87 | |||
88 | if (--clk->use_cnt == 0) { | ||
89 | if (clk_is_pll1(clk)) { /* PLL1 */ | ||
90 | chipcHw_pll1Disable(); | ||
91 | } else if (clk_is_pll2(clk)) { /* PLL2 */ | ||
92 | chipcHw_pll2Disable(); | ||
93 | } else if (clk_is_using_xtal(clk)) { /* source is crystal */ | ||
94 | if (!clk_is_primary(clk)) | ||
95 | chipcHw_bypassClockDisable(clk->csp_id); | ||
96 | } else { /* source is PLL */ | ||
97 | chipcHw_setClockDisable(clk->csp_id); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | if (clk->parent) | ||
102 | __clk_disable(clk->parent); | ||
103 | } | ||
104 | |||
105 | void clk_disable(struct clk *clk) | ||
106 | { | ||
107 | unsigned long flags; | ||
108 | |||
109 | if (!clk) | ||
110 | return; | ||
111 | |||
112 | spin_lock_irqsave(&clk_lock, flags); | ||
113 | __clk_disable(clk); | ||
114 | spin_unlock_irqrestore(&clk_lock, flags); | ||
115 | } | ||
116 | EXPORT_SYMBOL(clk_disable); | ||
117 | |||
118 | unsigned long clk_get_rate(struct clk *clk) | ||
119 | { | ||
120 | if (!clk) | ||
121 | return 0; | ||
122 | |||
123 | return clk->rate_hz; | ||
124 | } | ||
125 | EXPORT_SYMBOL(clk_get_rate); | ||
126 | |||
127 | long clk_round_rate(struct clk *clk, unsigned long rate) | ||
128 | { | ||
129 | unsigned long flags; | ||
130 | unsigned long actual; | ||
131 | unsigned long rate_hz; | ||
132 | |||
133 | if (!clk) | ||
134 | return -EINVAL; | ||
135 | |||
136 | if (!clk_is_programmable(clk)) | ||
137 | return -EINVAL; | ||
138 | |||
139 | if (clk->use_cnt) | ||
140 | return -EBUSY; | ||
141 | |||
142 | spin_lock_irqsave(&clk_lock, flags); | ||
143 | actual = clk->parent->rate_hz; | ||
144 | rate_hz = min(actual, rate); | ||
145 | spin_unlock_irqrestore(&clk_lock, flags); | ||
146 | |||
147 | return rate_hz; | ||
148 | } | ||
149 | EXPORT_SYMBOL(clk_round_rate); | ||
150 | |||
151 | int clk_set_rate(struct clk *clk, unsigned long rate) | ||
152 | { | ||
153 | unsigned long flags; | ||
154 | unsigned long actual; | ||
155 | unsigned long rate_hz; | ||
156 | |||
157 | if (!clk) | ||
158 | return -EINVAL; | ||
159 | |||
160 | if (!clk_is_programmable(clk)) | ||
161 | return -EINVAL; | ||
162 | |||
163 | if (clk->use_cnt) | ||
164 | return -EBUSY; | ||
165 | |||
166 | spin_lock_irqsave(&clk_lock, flags); | ||
167 | actual = clk->parent->rate_hz; | ||
168 | rate_hz = min(actual, rate); | ||
169 | rate_hz = chipcHw_setClockFrequency(clk->csp_id, rate_hz); | ||
170 | clk->rate_hz = rate_hz; | ||
171 | spin_unlock_irqrestore(&clk_lock, flags); | ||
172 | |||
173 | return 0; | ||
174 | } | ||
175 | EXPORT_SYMBOL(clk_set_rate); | ||
176 | |||
177 | struct clk *clk_get_parent(struct clk *clk) | ||
178 | { | ||
179 | if (!clk) | ||
180 | return NULL; | ||
181 | |||
182 | return clk->parent; | ||
183 | } | ||
184 | EXPORT_SYMBOL(clk_get_parent); | ||
185 | |||
186 | int clk_set_parent(struct clk *clk, struct clk *parent) | ||
187 | { | ||
188 | unsigned long flags; | ||
189 | struct clk *old_parent; | ||
190 | |||
191 | if (!clk || !parent) | ||
192 | return -EINVAL; | ||
193 | |||
194 | if (!clk_is_primary(parent) || !clk_is_bypassable(clk)) | ||
195 | return -EINVAL; | ||
196 | |||
197 | /* if more than one user, parent is not allowed */ | ||
198 | if (clk->use_cnt > 1) | ||
199 | return -EBUSY; | ||
200 | |||
201 | if (clk->parent == parent) | ||
202 | return 0; | ||
203 | |||
204 | spin_lock_irqsave(&clk_lock, flags); | ||
205 | old_parent = clk->parent; | ||
206 | clk->parent = parent; | ||
207 | if (clk_is_using_xtal(parent)) | ||
208 | clk->mode |= CLK_MODE_XTAL; | ||
209 | else | ||
210 | clk->mode &= (~CLK_MODE_XTAL); | ||
211 | |||
212 | /* if clock is active */ | ||
213 | if (clk->use_cnt != 0) { | ||
214 | clk->use_cnt--; | ||
215 | /* enable clock with the new parent */ | ||
216 | __clk_enable(clk); | ||
217 | /* disable the old parent */ | ||
218 | __clk_disable(old_parent); | ||
219 | } | ||
220 | spin_unlock_irqrestore(&clk_lock, flags); | ||
221 | |||
222 | return 0; | ||
223 | } | ||
224 | EXPORT_SYMBOL(clk_set_parent); | ||