blob: cd7581840d6bb4f264db18b0189b5c091d190496 [file] [log] [blame]
Stephen Boyd0275f932012-01-23 18:42:35 -08001/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/kernel.h>
14#include <linux/slab.h>
15#include <linux/init.h>
16#include <linux/err.h>
17#include <linux/module.h>
18#include <linux/spinlock.h>
19#include <linux/debugfs.h>
20#include <linux/list.h>
21#include <linux/seq_file.h>
22
23#include <mach/msm_xo.h>
24#include <mach/rpm.h>
Tianyi Gou41515e22011-09-01 19:37:43 -070025#include <mach/socinfo.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070026
27#include "rpm_resources.h"
28
29static DEFINE_SPINLOCK(msm_xo_lock);
30
31struct msm_xo {
32 unsigned votes[NUM_MSM_XO_MODES];
33 unsigned mode;
34 struct list_head voters;
35};
36
37struct msm_xo_voter {
38 const char *name;
39 unsigned mode;
40 struct msm_xo *xo;
41 struct list_head list;
42};
43
44static struct msm_xo msm_xo_sources[NUM_MSM_XO_IDS];
45
46#ifdef CONFIG_DEBUG_FS
47static const char *msm_xo_mode_to_str(unsigned mode)
48{
49 switch (mode) {
50 case MSM_XO_MODE_ON:
51 return "ON";
52 case MSM_XO_MODE_PIN_CTRL:
53 return "PIN";
54 case MSM_XO_MODE_OFF:
55 return "OFF";
56 default:
57 return "ERR";
58 }
59}
60
61static void msm_xo_dump_xo(struct seq_file *m, struct msm_xo *xo,
62 const char *name)
63{
64 struct msm_xo_voter *voter;
65
66 seq_printf(m, "%-20s%s\n", name, msm_xo_mode_to_str(xo->mode));
67 list_for_each_entry(voter, &xo->voters, list)
68 seq_printf(m, " %s %-16s %s\n",
69 xo->mode == voter->mode ? "*" : " ",
70 voter->name,
71 msm_xo_mode_to_str(voter->mode));
72}
73
74static int msm_xo_show_voters(struct seq_file *m, void *v)
75{
76 unsigned long flags;
77
78 spin_lock_irqsave(&msm_xo_lock, flags);
79 msm_xo_dump_xo(m, &msm_xo_sources[MSM_XO_TCXO_D0], "TCXO D0");
80 msm_xo_dump_xo(m, &msm_xo_sources[MSM_XO_TCXO_D1], "TCXO D1");
81 msm_xo_dump_xo(m, &msm_xo_sources[MSM_XO_TCXO_A0], "TCXO A0");
82 msm_xo_dump_xo(m, &msm_xo_sources[MSM_XO_TCXO_A1], "TCXO A1");
83 msm_xo_dump_xo(m, &msm_xo_sources[MSM_XO_TCXO_A2], "TCXO A2");
84 msm_xo_dump_xo(m, &msm_xo_sources[MSM_XO_CORE], "TCXO Core");
85 msm_xo_dump_xo(m, &msm_xo_sources[MSM_XO_PXO], "PXO during sleep");
Matt Wagantalled90b002011-12-12 21:22:43 -080086 msm_xo_dump_xo(m, &msm_xo_sources[MSM_XO_CXO], "CXO");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070087 spin_unlock_irqrestore(&msm_xo_lock, flags);
88
89 return 0;
90}
91
92static int msm_xo_voters_open(struct inode *inode, struct file *file)
93{
94 return single_open(file, msm_xo_show_voters, inode->i_private);
95}
96
97static const struct file_operations msm_xo_voters_ops = {
98 .open = msm_xo_voters_open,
99 .read = seq_read,
100 .llseek = seq_lseek,
101 .release = seq_release,
102};
103
104static int __init msm_xo_debugfs_init(void)
105{
106 struct dentry *entry;
107
108 entry = debugfs_create_file("xo_voters", S_IRUGO, NULL, NULL,
109 &msm_xo_voters_ops);
110 return IS_ERR(entry) ? PTR_ERR(entry) : 0;
111}
112late_initcall(msm_xo_debugfs_init);
113#endif
114
115static int msm_xo_update_vote(struct msm_xo *xo)
116{
117 int ret;
Vikram Mulukutla2021c002011-12-16 12:32:59 -0800118 unsigned vote, prev_vote = xo->mode, ctx_set;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700119 struct msm_rpm_iv_pair cmd;
120
121 if (xo->votes[MSM_XO_MODE_ON])
122 vote = MSM_XO_MODE_ON;
123 else if (xo->votes[MSM_XO_MODE_PIN_CTRL])
124 vote = MSM_XO_MODE_PIN_CTRL;
125 else
126 vote = MSM_XO_MODE_OFF;
127
128 if (vote == prev_vote)
129 return 0;
130
131 /*
132 * Change the vote here to simplify the TCXO logic. If the RPM
133 * command fails we'll rollback.
134 */
135 xo->mode = vote;
136
137 if (xo == &msm_xo_sources[MSM_XO_PXO]) {
138 cmd.id = MSM_RPM_ID_PXO_CLK;
139 cmd.value = msm_xo_sources[MSM_XO_PXO].mode ? 1 : 0;
140 ret = msm_rpmrs_set_noirq(MSM_RPM_CTX_SET_SLEEP, &cmd, 1);
Matt Wagantalled90b002011-12-12 21:22:43 -0800141 } else if (xo == &msm_xo_sources[MSM_XO_CXO]) {
142 cmd.id = MSM_RPM_ID_CXO_CLK;
143 cmd.value = msm_xo_sources[MSM_XO_CXO].mode ? 1 : 0;
Vikram Mulukutla2021c002011-12-16 12:32:59 -0800144 if (cpu_is_msm9615())
145 ctx_set = MSM_RPM_CTX_SET_SLEEP;
146 else
147 ctx_set = MSM_RPM_CTX_SET_0;
148 ret = msm_rpmrs_set_noirq(ctx_set, &cmd, 1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700149 } else {
150 cmd.id = MSM_RPM_ID_CXO_BUFFERS;
151 cmd.value = (msm_xo_sources[MSM_XO_TCXO_D0].mode << 0) |
152 (msm_xo_sources[MSM_XO_TCXO_D1].mode << 8) |
153 (msm_xo_sources[MSM_XO_TCXO_A0].mode << 16) |
154 (msm_xo_sources[MSM_XO_TCXO_A1].mode << 24) |
155 (msm_xo_sources[MSM_XO_TCXO_A2].mode << 28) |
Stephen Boyd0275f932012-01-23 18:42:35 -0800156 /*
157 * 8660 RPM has XO_CORE at bit 18 and 8960 RPM has
158 * XO_CORE at bit 20. Since the opposite bit is
159 * reserved in both cases, just set both and be
160 * done with it.
161 */
162 ((msm_xo_sources[MSM_XO_CORE].mode ? 1 : 0) << 20) |
163 ((msm_xo_sources[MSM_XO_CORE].mode ? 1 : 0) << 18);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700164 ret = msm_rpm_set_noirq(MSM_RPM_CTX_SET_0, &cmd, 1);
165 }
166
167 if (ret)
168 xo->mode = prev_vote;
169
170 return ret;
171}
172
173static int __msm_xo_mode_vote(struct msm_xo_voter *xo_voter, unsigned mode)
174{
175 int ret;
176 struct msm_xo *xo = xo_voter->xo;
177
178 if (xo_voter->mode == mode)
179 return 0;
180
181 xo->votes[mode]++;
182 xo->votes[xo_voter->mode]--;
183 ret = msm_xo_update_vote(xo);
184 if (ret) {
185 xo->votes[xo_voter->mode]++;
186 xo->votes[mode]--;
187 goto out;
188 }
189 xo_voter->mode = mode;
190out:
191 return ret;
192}
193
194/**
195 * msm_xo_mode_vote() - Vote for an XO to be ON, OFF, or under PIN_CTRL
196 * @xo_voter - Valid handle returned from msm_xo_get()
197 * @mode - Mode to vote for (ON, OFF, PIN_CTRL)
198 *
199 * Vote for an XO to be either ON, OFF, or under PIN_CTRL. Votes are
200 * aggregated with ON taking precedence over PIN_CTRL taking precedence
201 * over OFF.
202 *
203 * This function returns 0 on success or a negative error code on failure.
204 */
205int msm_xo_mode_vote(struct msm_xo_voter *xo_voter, enum msm_xo_modes mode)
206{
207 int ret;
208 unsigned long flags;
Tianyi Gouc5314912011-10-25 16:44:54 -0700209
210 if (!xo_voter)
Tianyi Gou41515e22011-09-01 19:37:43 -0700211 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700212
Tianyi Gouc5314912011-10-25 16:44:54 -0700213 if (mode >= NUM_MSM_XO_MODES || IS_ERR(xo_voter))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700214 return -EINVAL;
215
216 spin_lock_irqsave(&msm_xo_lock, flags);
217 ret = __msm_xo_mode_vote(xo_voter, mode);
218 spin_unlock_irqrestore(&msm_xo_lock, flags);
219
220 return ret;
221}
222EXPORT_SYMBOL(msm_xo_mode_vote);
223
224/**
225 * msm_xo_get() - Get a voting handle for an XO
226 * @xo_id - XO identifier
227 * @voter - Debug string to identify users
228 *
229 * XO voters vote for OFF by default. This function returns a pointer
230 * indicating success. An ERR_PTR is returned on failure.
231 *
232 * If XO voting is disabled, %NULL is returned.
233 */
234struct msm_xo_voter *msm_xo_get(enum msm_xo_ids xo_id, const char *voter)
235{
236 int ret;
237 unsigned long flags;
238 struct msm_xo_voter *xo_voter;
239
Tianyi Gouc5314912011-10-25 16:44:54 -0700240 /*
241 * TODO: Remove early return for 8064 once RPM XO voting support
Matt Wagantalled90b002011-12-12 21:22:43 -0800242 * is available. Remove early return for 8960 CXO once all voters
243 * for it are in place.
Tianyi Gouc5314912011-10-25 16:44:54 -0700244 */
Matt Wagantalled90b002011-12-12 21:22:43 -0800245 if (cpu_is_apq8064() || (cpu_is_msm8960() && xo_id == MSM_XO_CXO))
Tianyi Gou41515e22011-09-01 19:37:43 -0700246 return NULL;
247
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700248 if (xo_id >= NUM_MSM_XO_IDS) {
249 ret = -EINVAL;
250 goto err;
251 }
252
253 xo_voter = kzalloc(sizeof(*xo_voter), GFP_KERNEL);
254 if (!xo_voter) {
255 ret = -ENOMEM;
256 goto err;
257 }
258
259 xo_voter->name = kstrdup(voter, GFP_KERNEL);
260 if (!xo_voter->name) {
261 ret = -ENOMEM;
262 goto err_name;
263 }
264
265 xo_voter->xo = &msm_xo_sources[xo_id];
266
267 /* Voters vote for OFF by default */
268 spin_lock_irqsave(&msm_xo_lock, flags);
269 xo_voter->xo->votes[MSM_XO_MODE_OFF]++;
270 list_add(&xo_voter->list, &xo_voter->xo->voters);
271 spin_unlock_irqrestore(&msm_xo_lock, flags);
272
273 return xo_voter;
274
275err_name:
276 kfree(xo_voter);
277err:
278 return ERR_PTR(ret);
279}
280EXPORT_SYMBOL(msm_xo_get);
281
282/**
283 * msm_xo_put() - Release a voting handle
284 * @xo_voter - Valid handle returned from msm_xo_get()
285 *
286 * Release a reference to an XO voting handle. This also removes the voter's
287 * vote, therefore calling msm_xo_mode_vote(xo_voter, MSM_XO_MODE_OFF)
288 * beforehand is unnecessary.
289 */
290void msm_xo_put(struct msm_xo_voter *xo_voter)
291{
292 unsigned long flags;
293
Tianyi Gouc5314912011-10-25 16:44:54 -0700294 if (!xo_voter || IS_ERR(xo_voter))
295 return;
296
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700297 spin_lock_irqsave(&msm_xo_lock, flags);
298 __msm_xo_mode_vote(xo_voter, MSM_XO_MODE_OFF);
299 xo_voter->xo->votes[MSM_XO_MODE_OFF]--;
300 list_del(&xo_voter->list);
301 spin_unlock_irqrestore(&msm_xo_lock, flags);
302
303 kfree(xo_voter->name);
304 kfree(xo_voter);
305}
306EXPORT_SYMBOL(msm_xo_put);
307
308int __init msm_xo_init(void)
309{
310 int i;
Vikram Mulukutla2021c002011-12-16 12:32:59 -0800311 int ret = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700312 struct msm_rpm_iv_pair cmd[2];
313
314 for (i = 0; i < ARRAY_SIZE(msm_xo_sources); i++)
315 INIT_LIST_HEAD(&msm_xo_sources[i].voters);
316
Vikram Mulukutla2021c002011-12-16 12:32:59 -0800317 if (cpu_is_msm9615()) {
318 cmd[0].id = MSM_RPM_ID_CXO_CLK;
319 cmd[0].value = 1;
320 ret = msm_rpmrs_set(MSM_RPM_CTX_SET_0, cmd, 1);
321 if (ret)
322 goto out;
323
324 cmd[0].id = MSM_RPM_ID_CXO_CLK;
325 cmd[0].value = 0;
326 ret = msm_rpmrs_set(MSM_RPM_CTX_SET_SLEEP, cmd, 1);
327 goto out;
328 }
329
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700330 cmd[0].id = MSM_RPM_ID_PXO_CLK;
331 cmd[0].value = 1;
332 cmd[1].id = MSM_RPM_ID_CXO_BUFFERS;
333 cmd[1].value = 0;
334 ret = msm_rpmrs_set(MSM_RPM_CTX_SET_0, cmd, 2);
335 if (ret)
336 goto out;
337
338 cmd[0].id = MSM_RPM_ID_PXO_CLK;
339 cmd[0].value = 0;
340 ret = msm_rpmrs_set(MSM_RPM_CTX_SET_SLEEP, cmd, 1);
341 if (ret)
342 goto out;
343out:
344 return ret;
345}