blob: 86776d3857488b870299a7f0b9d70401a841f65e [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>
Stephen Boydb60f3862012-02-22 15:18:45 -080022#include <linux/uaccess.h>
23#include <linux/string.h>
Stephen Boyd5a190a82012-03-01 14:45:15 -080024#include <linux/clk.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070025
26#include <mach/msm_xo.h>
27#include <mach/rpm.h>
Tianyi Gou41515e22011-09-01 19:37:43 -070028#include <mach/socinfo.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070029
30#include "rpm_resources.h"
31
32static DEFINE_SPINLOCK(msm_xo_lock);
33
34struct msm_xo {
35 unsigned votes[NUM_MSM_XO_MODES];
36 unsigned mode;
37 struct list_head voters;
38};
39
40struct msm_xo_voter {
41 const char *name;
42 unsigned mode;
43 struct msm_xo *xo;
44 struct list_head list;
45};
46
47static struct msm_xo msm_xo_sources[NUM_MSM_XO_IDS];
48
49#ifdef CONFIG_DEBUG_FS
Stephen Boydb60f3862012-02-22 15:18:45 -080050static const char *msm_xo_to_str[NUM_MSM_XO_IDS] = {
51 [MSM_XO_TCXO_D0] = "D0",
52 [MSM_XO_TCXO_D1] = "D1",
53 [MSM_XO_TCXO_A0] = "A0",
54 [MSM_XO_TCXO_A1] = "A1",
55 [MSM_XO_TCXO_A2] = "A2",
56 [MSM_XO_CORE] = "CORE",
57};
58
59static const char *msm_xo_mode_to_str[NUM_MSM_XO_MODES] = {
60 [MSM_XO_MODE_ON] = "ON",
61 [MSM_XO_MODE_PIN_CTRL] = "PIN",
62 [MSM_XO_MODE_OFF] = "OFF",
63};
64
65static int msm_xo_debugfs_open(struct inode *inode, struct file *filp)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070066{
Stephen Boydb60f3862012-02-22 15:18:45 -080067 filp->private_data = inode->i_private;
68 return 0;
69}
70
71static ssize_t msm_xo_debugfs_read(struct file *filp, char __user *ubuf,
72 size_t cnt, loff_t *ppos)
73{
74 int r;
75 char buf[10];
76 struct msm_xo_voter *xo = filp->private_data;
77
78 r = snprintf(buf, sizeof(buf), "%s\n", msm_xo_mode_to_str[xo->mode]);
79 return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
80}
81
82static ssize_t msm_xo_debugfs_write(struct file *filp,
83 const char __user *ubuf, size_t cnt, loff_t *ppos)
84{
85 struct msm_xo_voter *xo = filp->private_data;
86 char buf[10], *b;
87 int i, ret;
88
89 if (cnt > sizeof(buf) - 1)
90 return -EINVAL;
91
92 if (copy_from_user(&buf, ubuf, cnt))
93 return -EFAULT;
94 buf[cnt] = '\0';
95 b = strstrip(buf);
96
97 for (i = 0; i < ARRAY_SIZE(msm_xo_mode_to_str); i++)
98 if (!strncasecmp(b, msm_xo_mode_to_str[i], sizeof(buf))) {
99 ret = msm_xo_mode_vote(xo, i);
100 return ret ? : cnt;
101 }
102
103 return -EINVAL;
104}
105
106static const struct file_operations msm_xo_debugfs_fops = {
107 .open = msm_xo_debugfs_open,
108 .read = msm_xo_debugfs_read,
109 .write = msm_xo_debugfs_write,
110};
111
112static struct dentry *xo_debugfs_root;
113static struct msm_xo_voter *xo_debugfs_voters[NUM_MSM_XO_IDS];
114
115static int __init msm_xo_init_debugfs_voters(void)
116{
117 int i;
118
119 xo_debugfs_root = debugfs_create_dir("msm_xo", NULL);
120 if (!xo_debugfs_root)
121 return -ENOMEM;
122
123 for (i = 0; i < ARRAY_SIZE(msm_xo_sources); i++) {
124 xo_debugfs_voters[i] = msm_xo_get(i, "debugfs");
125 if (IS_ERR(xo_debugfs_voters[i]))
126 goto err;
127 debugfs_create_file(msm_xo_to_str[i], S_IRUGO | S_IWUSR,
128 xo_debugfs_root, xo_debugfs_voters[i],
129 &msm_xo_debugfs_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700130 }
Stephen Boydb60f3862012-02-22 15:18:45 -0800131 return 0;
132err:
133 while (--i >= 0)
134 msm_xo_put(xo_debugfs_voters[i]);
135 debugfs_remove_recursive(xo_debugfs_root);
136 return -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700137}
138
139static void msm_xo_dump_xo(struct seq_file *m, struct msm_xo *xo,
140 const char *name)
141{
142 struct msm_xo_voter *voter;
143
Stephen Boydb60f3862012-02-22 15:18:45 -0800144 seq_printf(m, "CXO %-16s%s\n", name, msm_xo_mode_to_str[xo->mode]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700145 list_for_each_entry(voter, &xo->voters, list)
146 seq_printf(m, " %s %-16s %s\n",
147 xo->mode == voter->mode ? "*" : " ",
148 voter->name,
Stephen Boydb60f3862012-02-22 15:18:45 -0800149 msm_xo_mode_to_str[voter->mode]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700150}
151
152static int msm_xo_show_voters(struct seq_file *m, void *v)
153{
154 unsigned long flags;
Stephen Boydb60f3862012-02-22 15:18:45 -0800155 int i;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700156
157 spin_lock_irqsave(&msm_xo_lock, flags);
Stephen Boydb60f3862012-02-22 15:18:45 -0800158 for (i = 0; i < ARRAY_SIZE(msm_xo_sources); i++)
159 msm_xo_dump_xo(m, &msm_xo_sources[i], msm_xo_to_str[i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700160 spin_unlock_irqrestore(&msm_xo_lock, flags);
161
162 return 0;
163}
164
165static int msm_xo_voters_open(struct inode *inode, struct file *file)
166{
167 return single_open(file, msm_xo_show_voters, inode->i_private);
168}
169
170static const struct file_operations msm_xo_voters_ops = {
171 .open = msm_xo_voters_open,
172 .read = seq_read,
173 .llseek = seq_lseek,
174 .release = seq_release,
175};
176
177static int __init msm_xo_debugfs_init(void)
178{
Stephen Boydb60f3862012-02-22 15:18:45 -0800179 msm_xo_init_debugfs_voters();
180 if (!debugfs_create_file("xo_voters", S_IRUGO, NULL, NULL,
181 &msm_xo_voters_ops))
182 return -ENOMEM;
183 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700184}
Stephen Boyda70c5432012-02-10 14:31:45 -0800185#else
186static int __init msm_xo_debugfs_init(void) { return 0; }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700187#endif
188
189static int msm_xo_update_vote(struct msm_xo *xo)
190{
191 int ret;
Stephen Boyd47239d52012-01-26 14:38:40 -0800192 unsigned vote, prev_vote = xo->mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700193 struct msm_rpm_iv_pair cmd;
194
195 if (xo->votes[MSM_XO_MODE_ON])
196 vote = MSM_XO_MODE_ON;
197 else if (xo->votes[MSM_XO_MODE_PIN_CTRL])
198 vote = MSM_XO_MODE_PIN_CTRL;
199 else
200 vote = MSM_XO_MODE_OFF;
201
202 if (vote == prev_vote)
203 return 0;
204
205 /*
206 * Change the vote here to simplify the TCXO logic. If the RPM
207 * command fails we'll rollback.
208 */
209 xo->mode = vote;
Stephen Boyd47239d52012-01-26 14:38:40 -0800210 cmd.id = MSM_RPM_ID_CXO_BUFFERS;
211 cmd.value = (msm_xo_sources[MSM_XO_TCXO_D0].mode << 0) |
212 (msm_xo_sources[MSM_XO_TCXO_D1].mode << 8) |
213 (msm_xo_sources[MSM_XO_TCXO_A0].mode << 16) |
214 (msm_xo_sources[MSM_XO_TCXO_A1].mode << 24) |
215 (msm_xo_sources[MSM_XO_TCXO_A2].mode << 28) |
216 /*
217 * 8660 RPM has XO_CORE at bit 18 and 8960 RPM has
218 * XO_CORE at bit 20. Since the opposite bit is
219 * reserved in both cases, just set both and be
220 * done with it.
221 */
222 ((msm_xo_sources[MSM_XO_CORE].mode ? 1 : 0) << 20) |
223 ((msm_xo_sources[MSM_XO_CORE].mode ? 1 : 0) << 18);
224 ret = msm_rpm_set_noirq(MSM_RPM_CTX_SET_0, &cmd, 1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700225
226 if (ret)
227 xo->mode = prev_vote;
228
229 return ret;
230}
231
232static int __msm_xo_mode_vote(struct msm_xo_voter *xo_voter, unsigned mode)
233{
234 int ret;
235 struct msm_xo *xo = xo_voter->xo;
Stephen Boyd5a190a82012-03-01 14:45:15 -0800236 int is_d0 = xo == &msm_xo_sources[MSM_XO_TCXO_D0];
237 int needs_workaround = cpu_is_msm8960() || cpu_is_apq8064() ||
238 cpu_is_msm8930() || cpu_is_msm9615();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700239
240 if (xo_voter->mode == mode)
241 return 0;
242
243 xo->votes[mode]++;
244 xo->votes[xo_voter->mode]--;
245 ret = msm_xo_update_vote(xo);
246 if (ret) {
247 xo->votes[xo_voter->mode]++;
248 xo->votes[mode]--;
249 goto out;
250 }
Stephen Boyd5a190a82012-03-01 14:45:15 -0800251 /* TODO: Remove once RPM separates the concept of D0 and CXO */
252 if (is_d0 && needs_workaround) {
253 static struct clk *xo_clk;
254
255 if (!xo_clk) {
256 xo_clk = clk_get_sys("msm_xo", "xo");
257 BUG_ON(IS_ERR(xo_clk));
258 }
259 /* Ignore transitions from pin to on or vice versa */
260 if (mode && xo_voter->mode == MSM_XO_MODE_OFF)
261 clk_enable(xo_clk);
262 else if (!mode)
263 clk_disable(xo_clk);
264 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700265 xo_voter->mode = mode;
266out:
267 return ret;
268}
269
270/**
271 * msm_xo_mode_vote() - Vote for an XO to be ON, OFF, or under PIN_CTRL
272 * @xo_voter - Valid handle returned from msm_xo_get()
273 * @mode - Mode to vote for (ON, OFF, PIN_CTRL)
274 *
275 * Vote for an XO to be either ON, OFF, or under PIN_CTRL. Votes are
276 * aggregated with ON taking precedence over PIN_CTRL taking precedence
277 * over OFF.
278 *
279 * This function returns 0 on success or a negative error code on failure.
280 */
281int msm_xo_mode_vote(struct msm_xo_voter *xo_voter, enum msm_xo_modes mode)
282{
283 int ret;
284 unsigned long flags;
Tianyi Gouc5314912011-10-25 16:44:54 -0700285
286 if (!xo_voter)
Tianyi Gou41515e22011-09-01 19:37:43 -0700287 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700288
Tianyi Gouc5314912011-10-25 16:44:54 -0700289 if (mode >= NUM_MSM_XO_MODES || IS_ERR(xo_voter))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700290 return -EINVAL;
291
292 spin_lock_irqsave(&msm_xo_lock, flags);
293 ret = __msm_xo_mode_vote(xo_voter, mode);
294 spin_unlock_irqrestore(&msm_xo_lock, flags);
295
296 return ret;
297}
298EXPORT_SYMBOL(msm_xo_mode_vote);
299
300/**
301 * msm_xo_get() - Get a voting handle for an XO
302 * @xo_id - XO identifier
303 * @voter - Debug string to identify users
304 *
305 * XO voters vote for OFF by default. This function returns a pointer
306 * indicating success. An ERR_PTR is returned on failure.
307 *
308 * If XO voting is disabled, %NULL is returned.
309 */
310struct msm_xo_voter *msm_xo_get(enum msm_xo_ids xo_id, const char *voter)
311{
312 int ret;
313 unsigned long flags;
314 struct msm_xo_voter *xo_voter;
315
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700316 if (xo_id >= NUM_MSM_XO_IDS) {
317 ret = -EINVAL;
318 goto err;
319 }
320
321 xo_voter = kzalloc(sizeof(*xo_voter), GFP_KERNEL);
322 if (!xo_voter) {
323 ret = -ENOMEM;
324 goto err;
325 }
326
327 xo_voter->name = kstrdup(voter, GFP_KERNEL);
328 if (!xo_voter->name) {
329 ret = -ENOMEM;
330 goto err_name;
331 }
332
333 xo_voter->xo = &msm_xo_sources[xo_id];
334
335 /* Voters vote for OFF by default */
336 spin_lock_irqsave(&msm_xo_lock, flags);
337 xo_voter->xo->votes[MSM_XO_MODE_OFF]++;
338 list_add(&xo_voter->list, &xo_voter->xo->voters);
339 spin_unlock_irqrestore(&msm_xo_lock, flags);
340
341 return xo_voter;
342
343err_name:
344 kfree(xo_voter);
345err:
346 return ERR_PTR(ret);
347}
348EXPORT_SYMBOL(msm_xo_get);
349
350/**
351 * msm_xo_put() - Release a voting handle
352 * @xo_voter - Valid handle returned from msm_xo_get()
353 *
354 * Release a reference to an XO voting handle. This also removes the voter's
355 * vote, therefore calling msm_xo_mode_vote(xo_voter, MSM_XO_MODE_OFF)
356 * beforehand is unnecessary.
357 */
358void msm_xo_put(struct msm_xo_voter *xo_voter)
359{
360 unsigned long flags;
361
Tianyi Gouc5314912011-10-25 16:44:54 -0700362 if (!xo_voter || IS_ERR(xo_voter))
363 return;
364
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700365 spin_lock_irqsave(&msm_xo_lock, flags);
366 __msm_xo_mode_vote(xo_voter, MSM_XO_MODE_OFF);
367 xo_voter->xo->votes[MSM_XO_MODE_OFF]--;
368 list_del(&xo_voter->list);
369 spin_unlock_irqrestore(&msm_xo_lock, flags);
370
371 kfree(xo_voter->name);
372 kfree(xo_voter);
373}
374EXPORT_SYMBOL(msm_xo_put);
375
376int __init msm_xo_init(void)
377{
Stephen Boyd47239d52012-01-26 14:38:40 -0800378 int i, ret;
379 struct msm_rpm_iv_pair cmd[1];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700380
381 for (i = 0; i < ARRAY_SIZE(msm_xo_sources); i++)
382 INIT_LIST_HEAD(&msm_xo_sources[i].voters);
383
Stephen Boyd47239d52012-01-26 14:38:40 -0800384 cmd[0].id = MSM_RPM_ID_CXO_BUFFERS;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700385 cmd[0].value = 0;
Stephen Boyd47239d52012-01-26 14:38:40 -0800386 ret = msm_rpmrs_set(MSM_RPM_CTX_SET_0, cmd, ARRAY_SIZE(cmd));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700387 if (ret)
Stephen Boyd47239d52012-01-26 14:38:40 -0800388 return ret;
Stephen Boyda70c5432012-02-10 14:31:45 -0800389 msm_xo_debugfs_init();
Stephen Boyd47239d52012-01-26 14:38:40 -0800390 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700391}