diff options
author | Brian Swetland <swetland@google.com> | 2008-09-09 14:04:14 -0400 |
---|---|---|
committer | Brian Swetland <swetland@google.com> | 2008-10-22 05:41:00 -0400 |
commit | 600f7cfebeef74a6ba961c507079e3629da5dd7a (patch) | |
tree | bac251da969e1e4d2f277793e3ba906b25b7905e /arch/arm/mach-msm/clock.c | |
parent | bcc0f6af0798e60e7527485f7125ed26632ce698 (diff) |
[ARM] msm: clock: provide clk_*() api support for
Makes use of the proc_comm interface to provide clock control on
MSM7X01A family SoCs.
Signed-off-by: Brian Swetland <swetland@google.com>
Diffstat (limited to 'arch/arm/mach-msm/clock.c')
-rw-r--r-- | arch/arm/mach-msm/clock.c | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c new file mode 100644 index 000000000000..3b1ce36f1032 --- /dev/null +++ b/arch/arm/mach-msm/clock.c | |||
@@ -0,0 +1,218 @@ | |||
1 | /* arch/arm/mach-msm/clock.c | ||
2 | * | ||
3 | * Copyright (C) 2007 Google, Inc. | ||
4 | * Copyright (c) 2007 QUALCOMM Incorporated | ||
5 | * | ||
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 | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #include <linux/version.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/list.h> | ||
22 | #include <linux/err.h> | ||
23 | #include <linux/clk.h> | ||
24 | #include <linux/spinlock.h> | ||
25 | |||
26 | #include "clock.h" | ||
27 | #include "proc_comm.h" | ||
28 | |||
29 | static DEFINE_MUTEX(clocks_mutex); | ||
30 | static DEFINE_SPINLOCK(clocks_lock); | ||
31 | static LIST_HEAD(clocks); | ||
32 | |||
33 | /* | ||
34 | * glue for the proc_comm interface | ||
35 | */ | ||
36 | static inline int pc_clk_enable(unsigned id) | ||
37 | { | ||
38 | return msm_proc_comm(PCOM_CLKCTL_RPC_ENABLE, &id, NULL); | ||
39 | } | ||
40 | |||
41 | static inline void pc_clk_disable(unsigned id) | ||
42 | { | ||
43 | msm_proc_comm(PCOM_CLKCTL_RPC_DISABLE, &id, NULL); | ||
44 | } | ||
45 | |||
46 | static 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 | |||
51 | static 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 | |||
56 | static 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 | |||
61 | static 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 | |||
66 | static 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 | |||
74 | static 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 | |||
82 | static 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 | |||
88 | /* | ||
89 | * Standard clock functions defined in include/linux/clk.h | ||
90 | */ | ||
91 | struct clk *clk_get(struct device *dev, const char *id) | ||
92 | { | ||
93 | struct clk *clk; | ||
94 | |||
95 | mutex_lock(&clocks_mutex); | ||
96 | |||
97 | list_for_each_entry(clk, &clocks, list) | ||
98 | if (!strcmp(id, clk->name) && clk->dev == dev) | ||
99 | goto found_it; | ||
100 | |||
101 | list_for_each_entry(clk, &clocks, list) | ||
102 | if (!strcmp(id, clk->name) && clk->dev == NULL) | ||
103 | goto found_it; | ||
104 | |||
105 | clk = ERR_PTR(-ENOENT); | ||
106 | found_it: | ||
107 | mutex_unlock(&clocks_mutex); | ||
108 | return clk; | ||
109 | } | ||
110 | EXPORT_SYMBOL(clk_get); | ||
111 | |||
112 | void clk_put(struct clk *clk) | ||
113 | { | ||
114 | } | ||
115 | EXPORT_SYMBOL(clk_put); | ||
116 | |||
117 | int clk_enable(struct clk *clk) | ||
118 | { | ||
119 | unsigned long flags; | ||
120 | spin_lock_irqsave(&clocks_lock, flags); | ||
121 | clk->count++; | ||
122 | if (clk->count == 1) | ||
123 | pc_clk_enable(clk->id); | ||
124 | spin_unlock_irqrestore(&clocks_lock, flags); | ||
125 | return 0; | ||
126 | } | ||
127 | EXPORT_SYMBOL(clk_enable); | ||
128 | |||
129 | void clk_disable(struct clk *clk) | ||
130 | { | ||
131 | unsigned long flags; | ||
132 | spin_lock_irqsave(&clocks_lock, flags); | ||
133 | BUG_ON(clk->count == 0); | ||
134 | clk->count--; | ||
135 | if (clk->count == 0) | ||
136 | pc_clk_disable(clk->id); | ||
137 | spin_unlock_irqrestore(&clocks_lock, flags); | ||
138 | } | ||
139 | EXPORT_SYMBOL(clk_disable); | ||
140 | |||
141 | unsigned long clk_get_rate(struct clk *clk) | ||
142 | { | ||
143 | return pc_clk_get_rate(clk->id); | ||
144 | } | ||
145 | EXPORT_SYMBOL(clk_get_rate); | ||
146 | |||
147 | int clk_set_rate(struct clk *clk, unsigned long rate) | ||
148 | { | ||
149 | int ret; | ||
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 | } | ||
158 | EXPORT_SYMBOL(clk_set_rate); | ||
159 | |||
160 | int clk_set_parent(struct clk *clk, struct clk *parent) | ||
161 | { | ||
162 | return -ENOSYS; | ||
163 | } | ||
164 | EXPORT_SYMBOL(clk_set_parent); | ||
165 | |||
166 | struct clk *clk_get_parent(struct clk *clk) | ||
167 | { | ||
168 | return ERR_PTR(-ENOSYS); | ||
169 | } | ||
170 | EXPORT_SYMBOL(clk_get_parent); | ||
171 | |||
172 | int clk_set_flags(struct clk *clk, unsigned long flags) | ||
173 | { | ||
174 | if (clk == NULL || IS_ERR(clk)) | ||
175 | return -EINVAL; | ||
176 | return pc_clk_set_flags(clk->id, flags); | ||
177 | } | ||
178 | EXPORT_SYMBOL(clk_set_flags); | ||
179 | |||
180 | |||
181 | void __init msm_clock_init(void) | ||
182 | { | ||
183 | unsigned n; | ||
184 | |||
185 | spin_lock_init(&clocks_lock); | ||
186 | mutex_lock(&clocks_mutex); | ||
187 | for (n = 0; n < msm_num_clocks; n++) | ||
188 | list_add_tail(&msm_clocks[n].list, &clocks); | ||
189 | mutex_unlock(&clocks_mutex); | ||
190 | } | ||
191 | |||
192 | /* The bootloader and/or AMSS may have left various clocks enabled. | ||
193 | * Disable any clocks that belong to us (CLKFLAG_AUTO_OFF) but have | ||
194 | * not been explicitly enabled by a clk_enable() call. | ||
195 | */ | ||
196 | static int __init clock_late_init(void) | ||
197 | { | ||
198 | unsigned long flags; | ||
199 | struct clk *clk; | ||
200 | unsigned count = 0; | ||
201 | |||
202 | mutex_lock(&clocks_mutex); | ||
203 | list_for_each_entry(clk, &clocks, list) { | ||
204 | if (clk->flags & CLKFLAG_AUTO_OFF) { | ||
205 | spin_lock_irqsave(&clocks_lock, flags); | ||
206 | if (!clk->count) { | ||
207 | count++; | ||
208 | pc_clk_disable(clk->id); | ||
209 | } | ||
210 | spin_unlock_irqrestore(&clocks_lock, flags); | ||
211 | } | ||
212 | } | ||
213 | mutex_unlock(&clocks_mutex); | ||
214 | pr_info("clock_late_init() disabled %d unused clocks\n", count); | ||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | late_initcall(clock_late_init); | ||