aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-msm/clock.c
diff options
context:
space:
mode:
authorDaniel Walker <dwalker@codeaurora.org>2010-05-12 16:43:28 -0400
committerDaniel Walker <dwalker@codeaurora.org>2010-05-13 19:08:20 -0400
commit5e96da5d5074eae3b94d4abadfc114febb6e2a51 (patch)
tree4278052c5aaa63ae7af02fac8635c257139f3d6b /arch/arm/mach-msm/clock.c
parentec4d79255c684a74ade2f2394b9f9a669cee0036 (diff)
msm: generalize clock support.
The 'PCOM' method of clock control (commands issued to the radio CPU) is shared across several (but not all) Qualcomm SOCs. Generalize this clock mechanism so these other SOCs can be added. Signed-off-by: Gregory Bean <gbean@codeaurora.org> Signed-off-by: David Brown <davidb@codeaurora.org> Signed-off-by: Daniel Walker <dwalker@codeaurora.org> Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org> Signed-off-by: Stepan Moskovchenko <stepanm@codeaurora.org>
Diffstat (limited to 'arch/arm/mach-msm/clock.c')
-rw-r--r--arch/arm/mach-msm/clock.c257
1 files changed, 187 insertions, 70 deletions
diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c
index 3b1ce36f1032..34c6a52af060 100644
--- a/arch/arm/mach-msm/clock.c
+++ b/arch/arm/mach-msm/clock.c
@@ -1,7 +1,7 @@
1/* arch/arm/mach-msm/clock.c 1/* arch/arm/mach-msm/clock.c
2 * 2 *
3 * Copyright (C) 2007 Google, Inc. 3 * Copyright (C) 2007 Google, Inc.
4 * Copyright (c) 2007 QUALCOMM Incorporated 4 * Copyright (c) 2007-2010, Code Aurora Forum. All rights reserved.
5 * 5 *
6 * This software is licensed under the terms of the GNU General Public 6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and 7 * License version 2, as published by the Free Software Foundation, and
@@ -22,6 +22,10 @@
22#include <linux/err.h> 22#include <linux/err.h>
23#include <linux/clk.h> 23#include <linux/clk.h>
24#include <linux/spinlock.h> 24#include <linux/spinlock.h>
25#include <linux/debugfs.h>
26#include <linux/ctype.h>
27#include <linux/pm_qos_params.h>
28#include <mach/clk.h>
25 29
26#include "clock.h" 30#include "clock.h"
27#include "proc_comm.h" 31#include "proc_comm.h"
@@ -29,61 +33,15 @@
29static DEFINE_MUTEX(clocks_mutex); 33static DEFINE_MUTEX(clocks_mutex);
30static DEFINE_SPINLOCK(clocks_lock); 34static DEFINE_SPINLOCK(clocks_lock);
31static LIST_HEAD(clocks); 35static LIST_HEAD(clocks);
36struct clk *msm_clocks;
37unsigned msm_num_clocks;
32 38
33/* 39/*
34 * glue for the proc_comm interface 40 * Bitmap of enabled clocks, excluding ACPU which is always
41 * enabled
35 */ 42 */
36static inline int pc_clk_enable(unsigned id) 43static DECLARE_BITMAP(clock_map_enabled, NR_CLKS);
37{ 44static DEFINE_SPINLOCK(clock_map_lock);
38 return msm_proc_comm(PCOM_CLKCTL_RPC_ENABLE, &id, NULL);
39}
40
41static inline void pc_clk_disable(unsigned id)
42{
43 msm_proc_comm(PCOM_CLKCTL_RPC_DISABLE, &id, NULL);
44}
45
46static inline int pc_clk_set_rate(unsigned id, unsigned rate)
47{
48 return msm_proc_comm(PCOM_CLKCTL_RPC_SET_RATE, &id, &rate);
49}
50
51static inline int pc_clk_set_min_rate(unsigned id, unsigned rate)
52{
53 return msm_proc_comm(PCOM_CLKCTL_RPC_MIN_RATE, &id, &rate);
54}
55
56static inline int pc_clk_set_max_rate(unsigned id, unsigned rate)
57{
58 return msm_proc_comm(PCOM_CLKCTL_RPC_MAX_RATE, &id, &rate);
59}
60
61static inline int pc_clk_set_flags(unsigned id, unsigned flags)
62{
63 return msm_proc_comm(PCOM_CLKCTL_RPC_SET_FLAGS, &id, &flags);
64}
65
66static inline unsigned pc_clk_get_rate(unsigned id)
67{
68 if (msm_proc_comm(PCOM_CLKCTL_RPC_RATE, &id, NULL))
69 return 0;
70 else
71 return id;
72}
73
74static inline unsigned pc_clk_is_enabled(unsigned id)
75{
76 if (msm_proc_comm(PCOM_CLKCTL_RPC_ENABLED, &id, NULL))
77 return 0;
78 else
79 return id;
80}
81
82static inline int pc_pll_request(unsigned id, unsigned on)
83{
84 on = !!on;
85 return msm_proc_comm(PCOM_CLKCTL_RPC_PLL_REQUEST, &id, &on);
86}
87 45
88/* 46/*
89 * Standard clock functions defined in include/linux/clk.h 47 * Standard clock functions defined in include/linux/clk.h
@@ -119,8 +77,12 @@ int clk_enable(struct clk *clk)
119 unsigned long flags; 77 unsigned long flags;
120 spin_lock_irqsave(&clocks_lock, flags); 78 spin_lock_irqsave(&clocks_lock, flags);
121 clk->count++; 79 clk->count++;
122 if (clk->count == 1) 80 if (clk->count == 1) {
123 pc_clk_enable(clk->id); 81 clk->ops->enable(clk->id);
82 spin_lock(&clock_map_lock);
83 clock_map_enabled[BIT_WORD(clk->id)] |= BIT_MASK(clk->id);
84 spin_unlock(&clock_map_lock);
85 }
124 spin_unlock_irqrestore(&clocks_lock, flags); 86 spin_unlock_irqrestore(&clocks_lock, flags);
125 return 0; 87 return 0;
126} 88}
@@ -132,31 +94,54 @@ void clk_disable(struct clk *clk)
132 spin_lock_irqsave(&clocks_lock, flags); 94 spin_lock_irqsave(&clocks_lock, flags);
133 BUG_ON(clk->count == 0); 95 BUG_ON(clk->count == 0);
134 clk->count--; 96 clk->count--;
135 if (clk->count == 0) 97 if (clk->count == 0) {
136 pc_clk_disable(clk->id); 98 clk->ops->disable(clk->id);
99 spin_lock(&clock_map_lock);
100 clock_map_enabled[BIT_WORD(clk->id)] &= ~BIT_MASK(clk->id);
101 spin_unlock(&clock_map_lock);
102 }
137 spin_unlock_irqrestore(&clocks_lock, flags); 103 spin_unlock_irqrestore(&clocks_lock, flags);
138} 104}
139EXPORT_SYMBOL(clk_disable); 105EXPORT_SYMBOL(clk_disable);
140 106
107int clk_reset(struct clk *clk, enum clk_reset_action action)
108{
109 if (!clk->ops->reset)
110 clk->ops->reset = &pc_clk_reset;
111 return clk->ops->reset(clk->remote_id, action);
112}
113EXPORT_SYMBOL(clk_reset);
114
141unsigned long clk_get_rate(struct clk *clk) 115unsigned long clk_get_rate(struct clk *clk)
142{ 116{
143 return pc_clk_get_rate(clk->id); 117 return clk->ops->get_rate(clk->id);
144} 118}
145EXPORT_SYMBOL(clk_get_rate); 119EXPORT_SYMBOL(clk_get_rate);
146 120
147int clk_set_rate(struct clk *clk, unsigned long rate) 121int clk_set_rate(struct clk *clk, unsigned long rate)
148{ 122{
149 int ret; 123 return clk->ops->set_rate(clk->id, rate);
150 if (clk->flags & CLKFLAG_USE_MIN_MAX_TO_SET) {
151 ret = pc_clk_set_max_rate(clk->id, rate);
152 if (ret)
153 return ret;
154 return pc_clk_set_min_rate(clk->id, rate);
155 }
156 return pc_clk_set_rate(clk->id, rate);
157} 124}
158EXPORT_SYMBOL(clk_set_rate); 125EXPORT_SYMBOL(clk_set_rate);
159 126
127long clk_round_rate(struct clk *clk, unsigned long rate)
128{
129 return clk->ops->round_rate(clk->id, rate);
130}
131EXPORT_SYMBOL(clk_round_rate);
132
133int clk_set_min_rate(struct clk *clk, unsigned long rate)
134{
135 return clk->ops->set_min_rate(clk->id, rate);
136}
137EXPORT_SYMBOL(clk_set_min_rate);
138
139int clk_set_max_rate(struct clk *clk, unsigned long rate)
140{
141 return clk->ops->set_max_rate(clk->id, rate);
142}
143EXPORT_SYMBOL(clk_set_max_rate);
144
160int clk_set_parent(struct clk *clk, struct clk *parent) 145int clk_set_parent(struct clk *clk, struct clk *parent)
161{ 146{
162 return -ENOSYS; 147 return -ENOSYS;
@@ -173,22 +158,153 @@ int clk_set_flags(struct clk *clk, unsigned long flags)
173{ 158{
174 if (clk == NULL || IS_ERR(clk)) 159 if (clk == NULL || IS_ERR(clk))
175 return -EINVAL; 160 return -EINVAL;
176 return pc_clk_set_flags(clk->id, flags); 161 return clk->ops->set_flags(clk->id, flags);
177} 162}
178EXPORT_SYMBOL(clk_set_flags); 163EXPORT_SYMBOL(clk_set_flags);
179 164
165/* EBI1 is the only shared clock that several clients want to vote on as of
166 * this commit. If this changes in the future, then it might be better to
167 * make clk_min_rate handle the voting or make ebi1_clk_set_min_rate more
168 * generic to support different clocks.
169 */
170static struct clk *ebi1_clk;
180 171
181void __init msm_clock_init(void) 172static void __init set_clock_ops(struct clk *clk)
173{
174 if (!clk->ops) {
175 clk->ops = &clk_ops_pcom;
176 clk->id = clk->remote_id;
177 }
178}
179
180void __init msm_clock_init(struct clk *clock_tbl, unsigned num_clocks)
182{ 181{
183 unsigned n; 182 unsigned n;
184 183
185 spin_lock_init(&clocks_lock); 184 spin_lock_init(&clocks_lock);
186 mutex_lock(&clocks_mutex); 185 mutex_lock(&clocks_mutex);
187 for (n = 0; n < msm_num_clocks; n++) 186 msm_clocks = clock_tbl;
187 msm_num_clocks = num_clocks;
188 for (n = 0; n < msm_num_clocks; n++) {
189 set_clock_ops(&msm_clocks[n]);
188 list_add_tail(&msm_clocks[n].list, &clocks); 190 list_add_tail(&msm_clocks[n].list, &clocks);
191 }
189 mutex_unlock(&clocks_mutex); 192 mutex_unlock(&clocks_mutex);
193
194 ebi1_clk = clk_get(NULL, "ebi1_clk");
195 BUG_ON(ebi1_clk == NULL);
196
197}
198
199#if defined(CONFIG_DEBUG_FS)
200static struct clk *msm_clock_get_nth(unsigned index)
201{
202 if (index < msm_num_clocks)
203 return msm_clocks + index;
204 else
205 return 0;
206}
207
208static int clock_debug_rate_set(void *data, u64 val)
209{
210 struct clk *clock = data;
211 int ret;
212
213 /* Only increases to max rate will succeed, but that's actually good
214 * for debugging purposes. So we don't check for error. */
215 if (clock->flags & CLK_MAX)
216 clk_set_max_rate(clock, val);
217 if (clock->flags & CLK_MIN)
218 ret = clk_set_min_rate(clock, val);
219 else
220 ret = clk_set_rate(clock, val);
221 if (ret != 0)
222 printk(KERN_ERR "clk_set%s_rate failed (%d)\n",
223 (clock->flags & CLK_MIN) ? "_min" : "", ret);
224 return ret;
225}
226
227static int clock_debug_rate_get(void *data, u64 *val)
228{
229 struct clk *clock = data;
230 *val = clk_get_rate(clock);
231 return 0;
232}
233
234static int clock_debug_enable_set(void *data, u64 val)
235{
236 struct clk *clock = data;
237 int rc = 0;
238
239 if (val)
240 rc = clock->ops->enable(clock->id);
241 else
242 clock->ops->disable(clock->id);
243
244 return rc;
190} 245}
191 246
247static int clock_debug_enable_get(void *data, u64 *val)
248{
249 struct clk *clock = data;
250
251 *val = clock->ops->is_enabled(clock->id);
252
253 return 0;
254}
255
256static int clock_debug_local_get(void *data, u64 *val)
257{
258 struct clk *clock = data;
259
260 *val = clock->ops != &clk_ops_pcom;
261
262 return 0;
263}
264
265DEFINE_SIMPLE_ATTRIBUTE(clock_rate_fops, clock_debug_rate_get,
266 clock_debug_rate_set, "%llu\n");
267DEFINE_SIMPLE_ATTRIBUTE(clock_enable_fops, clock_debug_enable_get,
268 clock_debug_enable_set, "%llu\n");
269DEFINE_SIMPLE_ATTRIBUTE(clock_local_fops, clock_debug_local_get,
270 NULL, "%llu\n");
271
272static int __init clock_debug_init(void)
273{
274 struct dentry *dent_rate, *dent_enable, *dent_local;
275 struct clk *clock;
276 unsigned n = 0;
277 char temp[50], *ptr;
278
279 dent_rate = debugfs_create_dir("clk_rate", 0);
280 if (IS_ERR(dent_rate))
281 return PTR_ERR(dent_rate);
282
283 dent_enable = debugfs_create_dir("clk_enable", 0);
284 if (IS_ERR(dent_enable))
285 return PTR_ERR(dent_enable);
286
287 dent_local = debugfs_create_dir("clk_local", NULL);
288 if (IS_ERR(dent_local))
289 return PTR_ERR(dent_local);
290
291 while ((clock = msm_clock_get_nth(n++)) != 0) {
292 strncpy(temp, clock->dbg_name, ARRAY_SIZE(temp)-1);
293 for (ptr = temp; *ptr; ptr++)
294 *ptr = tolower(*ptr);
295 debugfs_create_file(temp, 0644, dent_rate,
296 clock, &clock_rate_fops);
297 debugfs_create_file(temp, 0644, dent_enable,
298 clock, &clock_enable_fops);
299 debugfs_create_file(temp, S_IRUGO, dent_local,
300 clock, &clock_local_fops);
301 }
302 return 0;
303}
304
305device_initcall(clock_debug_init);
306#endif
307
192/* The bootloader and/or AMSS may have left various clocks enabled. 308/* The bootloader and/or AMSS may have left various clocks enabled.
193 * Disable any clocks that belong to us (CLKFLAG_AUTO_OFF) but have 309 * Disable any clocks that belong to us (CLKFLAG_AUTO_OFF) but have
194 * not been explicitly enabled by a clk_enable() call. 310 * not been explicitly enabled by a clk_enable() call.
@@ -205,7 +321,7 @@ static int __init clock_late_init(void)
205 spin_lock_irqsave(&clocks_lock, flags); 321 spin_lock_irqsave(&clocks_lock, flags);
206 if (!clk->count) { 322 if (!clk->count) {
207 count++; 323 count++;
208 pc_clk_disable(clk->id); 324 clk->ops->auto_off(clk->id);
209 } 325 }
210 spin_unlock_irqrestore(&clocks_lock, flags); 326 spin_unlock_irqrestore(&clocks_lock, flags);
211 } 327 }
@@ -216,3 +332,4 @@ static int __init clock_late_init(void)
216} 332}
217 333
218late_initcall(clock_late_init); 334late_initcall(clock_late_init);
335