Commit | Line | Data |
---|---|---|
600f7cfe BS |
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); |