blob: 8eec807464f2393c9b3a33311c9c2690f14a4d9c [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 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#define pr_fmt(fmt) "%s: " fmt, __func__
14
15#include <linux/kernel.h>
16#include <linux/platform_device.h>
17#include <linux/err.h>
18#include <linux/regulator/driver.h>
19#include <linux/regulator/machine.h>
20#include <linux/clk.h>
21#include "footswitch.h"
22#include "proc_comm.h"
23
24/* PCOM power rail IDs */
25#define PCOM_FS_GRP 8
26#define PCOM_FS_GRP_2D 58
27#define PCOM_FS_MDP 14
28#define PCOM_FS_MFC 68
29#define PCOM_FS_ROTATOR 90
30#define PCOM_FS_VFE 41
31#define PCOM_FS_VPE 76
32
33#define PCOM_RAIL_MODE_AUTO 0
34#define PCOM_RAIL_MODE_MANUAL 1
35
36/**
37 * struct footswitch - Per-footswitch data and state
38 * @rdev: Regulator framework device
39 * @desc: Regulator descriptor
40 * @init_data: Regulator platform data
41 * @pcom_id: Proc-comm ID of the footswitch
42 * @is_enabled: Flag set when footswitch is enabled
43 * @is_manual: Flag set when footswitch is in manual proc-comm mode
Matt Wagantall49722712011-08-17 18:50:53 -070044 * @has_ahb_clk: Flag set if footswitched core has an ahb_clk
45 * @has_src_clk: Flag set if footswitched core has a src_clk
46 * @src_clk: Controls the core clock's rate
47 * @core_clk: Clocks the core
48 * @ahb_clk: Clocks the core's register interface
49 * @src_clk_init_rate: Rate to use for src_clk if it has not been set yet
50 * @is_rate_set: Flag set if core_clk's rate has been set
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070051 */
52struct footswitch {
53 struct regulator_dev *rdev;
54 struct regulator_desc desc;
55 struct regulator_init_data init_data;
56 unsigned pcom_id;
57 bool is_enabled;
58 bool is_manual;
Matt Wagantall49722712011-08-17 18:50:53 -070059 struct clk *src_clk;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070060 struct clk *core_clk;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070061 struct clk *ahb_clk;
Matt Wagantall49722712011-08-17 18:50:53 -070062 const bool has_ahb_clk;
63 const bool has_src_clk;
64 const int src_clk_init_rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070065 bool is_rate_set;
66};
67
68static inline int set_rail_mode(int pcom_id, int mode)
69{
70 int rc;
71
72 rc = msm_proc_comm(PCOM_CLKCTL_RPC_RAIL_CONTROL, &pcom_id, &mode);
73 if (!rc && pcom_id)
74 rc = -EINVAL;
75
76 return rc;
77}
78
79static inline int set_rail_state(int pcom_id, int state)
80{
81 int rc;
82
83 rc = msm_proc_comm(state, &pcom_id, NULL);
84 if (!rc && pcom_id)
85 rc = -EINVAL;
86
87 return rc;
88}
89
90static int enable_clocks(struct footswitch *fs)
91{
Matt Wagantall49722712011-08-17 18:50:53 -070092 fs->is_rate_set = !!(clk_get_rate(fs->src_clk));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070093 if (!fs->is_rate_set)
Matt Wagantall49722712011-08-17 18:50:53 -070094 clk_set_rate(fs->src_clk, fs->src_clk_init_rate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070095 clk_enable(fs->core_clk);
96
97 if (fs->ahb_clk)
98 clk_enable(fs->ahb_clk);
99
100 return 0;
101}
102
103static void disable_clocks(struct footswitch *fs)
104{
105 if (fs->ahb_clk)
106 clk_disable(fs->ahb_clk);
107 clk_disable(fs->core_clk);
108}
109
110static int footswitch_is_enabled(struct regulator_dev *rdev)
111{
112 struct footswitch *fs = rdev_get_drvdata(rdev);
113
114 return fs->is_enabled;
115}
116
117static int footswitch_enable(struct regulator_dev *rdev)
118{
119 struct footswitch *fs = rdev_get_drvdata(rdev);
120 int rc;
121
122 rc = enable_clocks(fs);
123 if (rc)
124 return rc;
125
126 rc = set_rail_state(fs->pcom_id, PCOM_CLKCTL_RPC_RAIL_ENABLE);
127 if (!rc)
128 fs->is_enabled = true;
129
130 disable_clocks(fs);
131
132 return rc;
133}
134
135static int footswitch_disable(struct regulator_dev *rdev)
136{
137 struct footswitch *fs = rdev_get_drvdata(rdev);
138 int rc;
139
140 rc = enable_clocks(fs);
141 if (rc)
142 return rc;
143
144 rc = set_rail_state(fs->pcom_id, PCOM_CLKCTL_RPC_RAIL_DISABLE);
145 if (!rc)
146 fs->is_enabled = false;
147
148 disable_clocks(fs);
149
150 return rc;
151}
152
153static struct regulator_ops footswitch_ops = {
154 .is_enabled = footswitch_is_enabled,
155 .enable = footswitch_enable,
156 .disable = footswitch_disable,
157};
158
Matt Wagantall49722712011-08-17 18:50:53 -0700159#define FOOTSWITCH(_id, _pcom_id, _name, _src_clk, _rate, _ahb_clk) \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700160 [_id] = { \
161 .desc = { \
162 .id = _id, \
163 .name = _name, \
164 .ops = &footswitch_ops, \
165 .type = REGULATOR_VOLTAGE, \
166 .owner = THIS_MODULE, \
167 }, \
168 .pcom_id = _pcom_id, \
Matt Wagantall49722712011-08-17 18:50:53 -0700169 .has_src_clk = _src_clk, \
170 .src_clk_init_rate = _rate, \
171 .has_ahb_clk = _ahb_clk, \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700172 }
173static struct footswitch footswitches[] = {
Matt Wagantall49722712011-08-17 18:50:53 -0700174 FOOTSWITCH(FS_GFX3D, PCOM_FS_GRP,
175 "fs_gfx3d", true, 24576000, true),
176 FOOTSWITCH(FS_GFX2D0, PCOM_FS_GRP_2D,
177 "fs_gfx2d0", false, 24576000, true),
178 FOOTSWITCH(FS_MDP, PCOM_FS_MDP,
179 "fs_mdp", false, 24576000, true),
180 FOOTSWITCH(FS_MFC, PCOM_FS_MFC,
181 "fs_mfc", false, 24576000, true),
182 FOOTSWITCH(FS_ROT, PCOM_FS_ROTATOR,
183 "fs_rot", false, 0, true),
184 FOOTSWITCH(FS_VFE, PCOM_FS_VFE,
185 "fs_vfe", false, 24576000, true),
186 FOOTSWITCH(FS_VPE, PCOM_FS_VPE,
187 "fs_vpe", false, 24576000, false),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700188};
189
Matt Wagantall49722712011-08-17 18:50:53 -0700190static int get_clocks(struct device *dev, struct footswitch *fs)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700191{
192 int rc;
193
194 /*
195 * Some SoCs may not have a separate rate-settable clock.
196 * If one can't be found, try to use the core clock for
197 * rate-setting instead.
198 */
Matt Wagantall49722712011-08-17 18:50:53 -0700199 if (fs->has_src_clk) {
200 fs->src_clk = clk_get(dev, "src_clk");
201 if (IS_ERR(fs->src_clk))
202 fs->src_clk = clk_get(dev, "core_clk");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700203 } else {
Matt Wagantall49722712011-08-17 18:50:53 -0700204 fs->src_clk = clk_get(dev, "core_clk");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700205 }
Matt Wagantall49722712011-08-17 18:50:53 -0700206 if (IS_ERR(fs->src_clk)) {
207 pr_err("clk_get(src_clk) failed\n");
208 rc = PTR_ERR(fs->src_clk);
209 goto err_src_clk;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700210 }
211
Matt Wagantall49722712011-08-17 18:50:53 -0700212 fs->core_clk = clk_get(dev, "core_clk");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700213 if (IS_ERR(fs->core_clk)) {
Matt Wagantall49722712011-08-17 18:50:53 -0700214 pr_err("clk_get(core_clk) failed\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700215 rc = PTR_ERR(fs->core_clk);
216 goto err_core_clk;
217 }
218
Matt Wagantall49722712011-08-17 18:50:53 -0700219 if (fs->has_ahb_clk) {
220 fs->ahb_clk = clk_get(dev, "iface_clk");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700221 if (IS_ERR(fs->ahb_clk)) {
Matt Wagantall49722712011-08-17 18:50:53 -0700222 pr_err("clk_get(iface_clk) failed\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700223 rc = PTR_ERR(fs->ahb_clk);
224 goto err_ahb_clk;
225 }
226 }
227
228 return 0;
229
230err_ahb_clk:
231 clk_put(fs->core_clk);
232err_core_clk:
Matt Wagantall49722712011-08-17 18:50:53 -0700233 clk_put(fs->src_clk);
234err_src_clk:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700235 return rc;
236}
237
238static void put_clocks(struct footswitch *fs)
239{
Matt Wagantall49722712011-08-17 18:50:53 -0700240 clk_put(fs->src_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700241 clk_put(fs->core_clk);
242 clk_put(fs->ahb_clk);
243}
244
245static int footswitch_probe(struct platform_device *pdev)
246{
247 struct footswitch *fs;
248 struct regulator_init_data *init_data;
249 int rc;
250
251 if (pdev == NULL)
252 return -EINVAL;
253
254 if (pdev->id >= MAX_FS)
255 return -ENODEV;
256
257 fs = &footswitches[pdev->id];
258 if (!fs->is_manual) {
259 pr_err("%s is not in manual mode\n", fs->desc.name);
260 return -EINVAL;
261 }
262 init_data = pdev->dev.platform_data;
263
Matt Wagantall49722712011-08-17 18:50:53 -0700264 rc = get_clocks(&pdev->dev, fs);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700265 if (rc)
266 return rc;
267
268 fs->rdev = regulator_register(&fs->desc, &pdev->dev, init_data, fs);
269 if (IS_ERR(fs->rdev)) {
270 pr_err("regulator_register(%s) failed\n", fs->desc.name);
271 rc = PTR_ERR(fs->rdev);
272 goto err_register;
273 }
274
275 return 0;
276
277err_register:
278 put_clocks(fs);
279
280 return rc;
281}
282
283static int __devexit footswitch_remove(struct platform_device *pdev)
284{
285 struct footswitch *fs = &footswitches[pdev->id];
286
287 regulator_unregister(fs->rdev);
288 set_rail_mode(fs->pcom_id, PCOM_RAIL_MODE_AUTO);
289 put_clocks(fs);
290
291 return 0;
292}
293
294static struct platform_driver footswitch_driver = {
295 .probe = footswitch_probe,
296 .remove = __devexit_p(footswitch_remove),
297 .driver = {
298 .name = "footswitch-pcom",
299 .owner = THIS_MODULE,
300 },
301};
302
303static int __init footswitch_init(void)
304{
305 struct footswitch *fs;
306 int ret;
307
308 /*
309 * Enable all footswitches in manual mode (ie. not controlled along
310 * with pcom clocks).
311 */
312 for (fs = footswitches; fs < footswitches + ARRAY_SIZE(footswitches);
313 fs++) {
314 set_rail_state(fs->pcom_id, PCOM_CLKCTL_RPC_RAIL_ENABLE);
315 ret = set_rail_mode(fs->pcom_id, PCOM_RAIL_MODE_MANUAL);
316 if (!ret)
317 fs->is_manual = 1;
318 }
319
320 return platform_driver_register(&footswitch_driver);
321}
322subsys_initcall(footswitch_init);
323
324static void __exit footswitch_exit(void)
325{
326 platform_driver_unregister(&footswitch_driver);
327}
328module_exit(footswitch_exit);
329
Matt Wagantalldf9d32a2011-09-09 13:50:06 -0700330MODULE_LICENSE("GPL v2");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700331MODULE_DESCRIPTION("proc_comm rail footswitch");
332MODULE_ALIAS("platform:footswitch-pcom");