blob: 5b66b96d8a8c8d6d1f41961afb1871769eb2530d [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
2 *
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>
25
26#include "rpm_resources.h"
27
28static DEFINE_SPINLOCK(msm_xo_lock);
29
30struct msm_xo {
31 unsigned votes[NUM_MSM_XO_MODES];
32 unsigned mode;
33 struct list_head voters;
34};
35
36struct msm_xo_voter {
37 const char *name;
38 unsigned mode;
39 struct msm_xo *xo;
40 struct list_head list;
41};
42
43static struct msm_xo msm_xo_sources[NUM_MSM_XO_IDS];
44
45#ifdef CONFIG_DEBUG_FS
46static const char *msm_xo_mode_to_str(unsigned mode)
47{
48 switch (mode) {
49 case MSM_XO_MODE_ON:
50 return "ON";
51 case MSM_XO_MODE_PIN_CTRL:
52 return "PIN";
53 case MSM_XO_MODE_OFF:
54 return "OFF";
55 default:
56 return "ERR";
57 }
58}
59
60static void msm_xo_dump_xo(struct seq_file *m, struct msm_xo *xo,
61 const char *name)
62{
63 struct msm_xo_voter *voter;
64
65 seq_printf(m, "%-20s%s\n", name, msm_xo_mode_to_str(xo->mode));
66 list_for_each_entry(voter, &xo->voters, list)
67 seq_printf(m, " %s %-16s %s\n",
68 xo->mode == voter->mode ? "*" : " ",
69 voter->name,
70 msm_xo_mode_to_str(voter->mode));
71}
72
73static int msm_xo_show_voters(struct seq_file *m, void *v)
74{
75 unsigned long flags;
76
77 spin_lock_irqsave(&msm_xo_lock, flags);
78 msm_xo_dump_xo(m, &msm_xo_sources[MSM_XO_TCXO_D0], "TCXO D0");
79 msm_xo_dump_xo(m, &msm_xo_sources[MSM_XO_TCXO_D1], "TCXO D1");
80 msm_xo_dump_xo(m, &msm_xo_sources[MSM_XO_TCXO_A0], "TCXO A0");
81 msm_xo_dump_xo(m, &msm_xo_sources[MSM_XO_TCXO_A1], "TCXO A1");
82 msm_xo_dump_xo(m, &msm_xo_sources[MSM_XO_TCXO_A2], "TCXO A2");
83 msm_xo_dump_xo(m, &msm_xo_sources[MSM_XO_CORE], "TCXO Core");
84 msm_xo_dump_xo(m, &msm_xo_sources[MSM_XO_PXO], "PXO during sleep");
85 spin_unlock_irqrestore(&msm_xo_lock, flags);
86
87 return 0;
88}
89
90static int msm_xo_voters_open(struct inode *inode, struct file *file)
91{
92 return single_open(file, msm_xo_show_voters, inode->i_private);
93}
94
95static const struct file_operations msm_xo_voters_ops = {
96 .open = msm_xo_voters_open,
97 .read = seq_read,
98 .llseek = seq_lseek,
99 .release = seq_release,
100};
101
102static int __init msm_xo_debugfs_init(void)
103{
104 struct dentry *entry;
105
106 entry = debugfs_create_file("xo_voters", S_IRUGO, NULL, NULL,
107 &msm_xo_voters_ops);
108 return IS_ERR(entry) ? PTR_ERR(entry) : 0;
109}
110late_initcall(msm_xo_debugfs_init);
111#endif
112
113static int msm_xo_update_vote(struct msm_xo *xo)
114{
115 int ret;
116 unsigned vote, prev_vote = xo->mode;
117 struct msm_rpm_iv_pair cmd;
118
119 if (xo->votes[MSM_XO_MODE_ON])
120 vote = MSM_XO_MODE_ON;
121 else if (xo->votes[MSM_XO_MODE_PIN_CTRL])
122 vote = MSM_XO_MODE_PIN_CTRL;
123 else
124 vote = MSM_XO_MODE_OFF;
125
126 if (vote == prev_vote)
127 return 0;
128
129 /*
130 * Change the vote here to simplify the TCXO logic. If the RPM
131 * command fails we'll rollback.
132 */
133 xo->mode = vote;
134
135 if (xo == &msm_xo_sources[MSM_XO_PXO]) {
136 cmd.id = MSM_RPM_ID_PXO_CLK;
137 cmd.value = msm_xo_sources[MSM_XO_PXO].mode ? 1 : 0;
138 ret = msm_rpmrs_set_noirq(MSM_RPM_CTX_SET_SLEEP, &cmd, 1);
139 } else {
140 cmd.id = MSM_RPM_ID_CXO_BUFFERS;
141 cmd.value = (msm_xo_sources[MSM_XO_TCXO_D0].mode << 0) |
142 (msm_xo_sources[MSM_XO_TCXO_D1].mode << 8) |
143 (msm_xo_sources[MSM_XO_TCXO_A0].mode << 16) |
144 (msm_xo_sources[MSM_XO_TCXO_A1].mode << 24) |
145 (msm_xo_sources[MSM_XO_TCXO_A2].mode << 28) |
146 ((msm_xo_sources[MSM_XO_CORE].mode ? 1 : 0) << 20);
147 ret = msm_rpm_set_noirq(MSM_RPM_CTX_SET_0, &cmd, 1);
148 }
149
150 if (ret)
151 xo->mode = prev_vote;
152
153 return ret;
154}
155
156static int __msm_xo_mode_vote(struct msm_xo_voter *xo_voter, unsigned mode)
157{
158 int ret;
159 struct msm_xo *xo = xo_voter->xo;
160
161 if (xo_voter->mode == mode)
162 return 0;
163
164 xo->votes[mode]++;
165 xo->votes[xo_voter->mode]--;
166 ret = msm_xo_update_vote(xo);
167 if (ret) {
168 xo->votes[xo_voter->mode]++;
169 xo->votes[mode]--;
170 goto out;
171 }
172 xo_voter->mode = mode;
173out:
174 return ret;
175}
176
177/**
178 * msm_xo_mode_vote() - Vote for an XO to be ON, OFF, or under PIN_CTRL
179 * @xo_voter - Valid handle returned from msm_xo_get()
180 * @mode - Mode to vote for (ON, OFF, PIN_CTRL)
181 *
182 * Vote for an XO to be either ON, OFF, or under PIN_CTRL. Votes are
183 * aggregated with ON taking precedence over PIN_CTRL taking precedence
184 * over OFF.
185 *
186 * This function returns 0 on success or a negative error code on failure.
187 */
188int msm_xo_mode_vote(struct msm_xo_voter *xo_voter, enum msm_xo_modes mode)
189{
190 int ret;
191 unsigned long flags;
192
193 if (mode >= NUM_MSM_XO_MODES)
194 return -EINVAL;
195
196 spin_lock_irqsave(&msm_xo_lock, flags);
197 ret = __msm_xo_mode_vote(xo_voter, mode);
198 spin_unlock_irqrestore(&msm_xo_lock, flags);
199
200 return ret;
201}
202EXPORT_SYMBOL(msm_xo_mode_vote);
203
204/**
205 * msm_xo_get() - Get a voting handle for an XO
206 * @xo_id - XO identifier
207 * @voter - Debug string to identify users
208 *
209 * XO voters vote for OFF by default. This function returns a pointer
210 * indicating success. An ERR_PTR is returned on failure.
211 *
212 * If XO voting is disabled, %NULL is returned.
213 */
214struct msm_xo_voter *msm_xo_get(enum msm_xo_ids xo_id, const char *voter)
215{
216 int ret;
217 unsigned long flags;
218 struct msm_xo_voter *xo_voter;
219
220 if (xo_id >= NUM_MSM_XO_IDS) {
221 ret = -EINVAL;
222 goto err;
223 }
224
225 xo_voter = kzalloc(sizeof(*xo_voter), GFP_KERNEL);
226 if (!xo_voter) {
227 ret = -ENOMEM;
228 goto err;
229 }
230
231 xo_voter->name = kstrdup(voter, GFP_KERNEL);
232 if (!xo_voter->name) {
233 ret = -ENOMEM;
234 goto err_name;
235 }
236
237 xo_voter->xo = &msm_xo_sources[xo_id];
238
239 /* Voters vote for OFF by default */
240 spin_lock_irqsave(&msm_xo_lock, flags);
241 xo_voter->xo->votes[MSM_XO_MODE_OFF]++;
242 list_add(&xo_voter->list, &xo_voter->xo->voters);
243 spin_unlock_irqrestore(&msm_xo_lock, flags);
244
245 return xo_voter;
246
247err_name:
248 kfree(xo_voter);
249err:
250 return ERR_PTR(ret);
251}
252EXPORT_SYMBOL(msm_xo_get);
253
254/**
255 * msm_xo_put() - Release a voting handle
256 * @xo_voter - Valid handle returned from msm_xo_get()
257 *
258 * Release a reference to an XO voting handle. This also removes the voter's
259 * vote, therefore calling msm_xo_mode_vote(xo_voter, MSM_XO_MODE_OFF)
260 * beforehand is unnecessary.
261 */
262void msm_xo_put(struct msm_xo_voter *xo_voter)
263{
264 unsigned long flags;
265
266 spin_lock_irqsave(&msm_xo_lock, flags);
267 __msm_xo_mode_vote(xo_voter, MSM_XO_MODE_OFF);
268 xo_voter->xo->votes[MSM_XO_MODE_OFF]--;
269 list_del(&xo_voter->list);
270 spin_unlock_irqrestore(&msm_xo_lock, flags);
271
272 kfree(xo_voter->name);
273 kfree(xo_voter);
274}
275EXPORT_SYMBOL(msm_xo_put);
276
277int __init msm_xo_init(void)
278{
279 int i;
280 int ret;
281 struct msm_rpm_iv_pair cmd[2];
282
283 for (i = 0; i < ARRAY_SIZE(msm_xo_sources); i++)
284 INIT_LIST_HEAD(&msm_xo_sources[i].voters);
285
286 cmd[0].id = MSM_RPM_ID_PXO_CLK;
287 cmd[0].value = 1;
288 cmd[1].id = MSM_RPM_ID_CXO_BUFFERS;
289 cmd[1].value = 0;
290 ret = msm_rpmrs_set(MSM_RPM_CTX_SET_0, cmd, 2);
291 if (ret)
292 goto out;
293
294 cmd[0].id = MSM_RPM_ID_PXO_CLK;
295 cmd[0].value = 0;
296 ret = msm_rpmrs_set(MSM_RPM_CTX_SET_SLEEP, cmd, 1);
297 if (ret)
298 goto out;
299out:
300 return ret;
301}