blob: 673253b9085af2bacf26ab881dd1fc0390361ecc [file] [log] [blame]
Matt Wagantall1a7ee892012-01-17 18:56:28 -08001/* Copyright (c) 2011-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#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>
Taniya Das78b72d62011-12-02 15:54:21 +053021#include <mach/socinfo.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070022#include "footswitch.h"
23#include "proc_comm.h"
24
25/* PCOM power rail IDs */
26#define PCOM_FS_GRP 8
27#define PCOM_FS_GRP_2D 58
28#define PCOM_FS_MDP 14
29#define PCOM_FS_MFC 68
30#define PCOM_FS_ROTATOR 90
31#define PCOM_FS_VFE 41
32#define PCOM_FS_VPE 76
33
34#define PCOM_RAIL_MODE_AUTO 0
35#define PCOM_RAIL_MODE_MANUAL 1
36
37/**
38 * struct footswitch - Per-footswitch data and state
39 * @rdev: Regulator framework device
40 * @desc: Regulator descriptor
41 * @init_data: Regulator platform data
42 * @pcom_id: Proc-comm ID of the footswitch
43 * @is_enabled: Flag set when footswitch is enabled
44 * @is_manual: Flag set when footswitch is in manual proc-comm mode
Matt Wagantall49722712011-08-17 18:50:53 -070045 * @has_ahb_clk: Flag set if footswitched core has an ahb_clk
46 * @has_src_clk: Flag set if footswitched core has a src_clk
47 * @src_clk: Controls the core clock's rate
48 * @core_clk: Clocks the core
49 * @ahb_clk: Clocks the core's register interface
50 * @src_clk_init_rate: Rate to use for src_clk if it has not been set yet
51 * @is_rate_set: Flag set if core_clk's rate has been set
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070052 */
53struct footswitch {
54 struct regulator_dev *rdev;
55 struct regulator_desc desc;
56 struct regulator_init_data init_data;
57 unsigned pcom_id;
58 bool is_enabled;
59 bool is_manual;
Matt Wagantall49722712011-08-17 18:50:53 -070060 struct clk *src_clk;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070061 struct clk *core_clk;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070062 struct clk *ahb_clk;
Matt Wagantall49722712011-08-17 18:50:53 -070063 const bool has_ahb_clk;
64 const bool has_src_clk;
65 const int src_clk_init_rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070066 bool is_rate_set;
67};
68
69static inline int set_rail_mode(int pcom_id, int mode)
70{
71 int rc;
72
73 rc = msm_proc_comm(PCOM_CLKCTL_RPC_RAIL_CONTROL, &pcom_id, &mode);
74 if (!rc && pcom_id)
75 rc = -EINVAL;
76
77 return rc;
78}
79
80static inline int set_rail_state(int pcom_id, int state)
81{
82 int rc;
83
84 rc = msm_proc_comm(state, &pcom_id, NULL);
85 if (!rc && pcom_id)
86 rc = -EINVAL;
87
88 return rc;
89}
90
91static int enable_clocks(struct footswitch *fs)
92{
Matt Wagantall49722712011-08-17 18:50:53 -070093 fs->is_rate_set = !!(clk_get_rate(fs->src_clk));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070094 if (!fs->is_rate_set)
Matt Wagantall49722712011-08-17 18:50:53 -070095 clk_set_rate(fs->src_clk, fs->src_clk_init_rate);
Matt Wagantall1a7ee892012-01-17 18:56:28 -080096 clk_prepare_enable(fs->core_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070097
98 if (fs->ahb_clk)
Matt Wagantall1a7ee892012-01-17 18:56:28 -080099 clk_prepare_enable(fs->ahb_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700100
101 return 0;
102}
103
104static void disable_clocks(struct footswitch *fs)
105{
106 if (fs->ahb_clk)
Matt Wagantall1a7ee892012-01-17 18:56:28 -0800107 clk_disable_unprepare(fs->ahb_clk);
108 clk_disable_unprepare(fs->core_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700109}
110
111static int footswitch_is_enabled(struct regulator_dev *rdev)
112{
113 struct footswitch *fs = rdev_get_drvdata(rdev);
114
115 return fs->is_enabled;
116}
117
118static int footswitch_enable(struct regulator_dev *rdev)
119{
120 struct footswitch *fs = rdev_get_drvdata(rdev);
121 int rc;
122
123 rc = enable_clocks(fs);
124 if (rc)
125 return rc;
126
127 rc = set_rail_state(fs->pcom_id, PCOM_CLKCTL_RPC_RAIL_ENABLE);
128 if (!rc)
129 fs->is_enabled = true;
130
131 disable_clocks(fs);
132
133 return rc;
134}
135
136static int footswitch_disable(struct regulator_dev *rdev)
137{
138 struct footswitch *fs = rdev_get_drvdata(rdev);
139 int rc;
140
141 rc = enable_clocks(fs);
142 if (rc)
143 return rc;
144
145 rc = set_rail_state(fs->pcom_id, PCOM_CLKCTL_RPC_RAIL_DISABLE);
146 if (!rc)
147 fs->is_enabled = false;
148
149 disable_clocks(fs);
150
151 return rc;
152}
153
154static struct regulator_ops footswitch_ops = {
155 .is_enabled = footswitch_is_enabled,
156 .enable = footswitch_enable,
157 .disable = footswitch_disable,
158};
159
Matt Wagantall49722712011-08-17 18:50:53 -0700160#define FOOTSWITCH(_id, _pcom_id, _name, _src_clk, _rate, _ahb_clk) \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700161 [_id] = { \
162 .desc = { \
163 .id = _id, \
164 .name = _name, \
165 .ops = &footswitch_ops, \
166 .type = REGULATOR_VOLTAGE, \
167 .owner = THIS_MODULE, \
168 }, \
169 .pcom_id = _pcom_id, \
Matt Wagantall49722712011-08-17 18:50:53 -0700170 .has_src_clk = _src_clk, \
171 .src_clk_init_rate = _rate, \
172 .has_ahb_clk = _ahb_clk, \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700173 }
174static struct footswitch footswitches[] = {
Matt Wagantall49722712011-08-17 18:50:53 -0700175 FOOTSWITCH(FS_GFX3D, PCOM_FS_GRP,
176 "fs_gfx3d", true, 24576000, true),
177 FOOTSWITCH(FS_GFX2D0, PCOM_FS_GRP_2D,
178 "fs_gfx2d0", false, 24576000, true),
179 FOOTSWITCH(FS_MDP, PCOM_FS_MDP,
180 "fs_mdp", false, 24576000, true),
181 FOOTSWITCH(FS_MFC, PCOM_FS_MFC,
182 "fs_mfc", false, 24576000, true),
183 FOOTSWITCH(FS_ROT, PCOM_FS_ROTATOR,
184 "fs_rot", false, 0, true),
185 FOOTSWITCH(FS_VFE, PCOM_FS_VFE,
186 "fs_vfe", false, 24576000, true),
187 FOOTSWITCH(FS_VPE, PCOM_FS_VPE,
188 "fs_vpe", false, 24576000, false),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700189};
190
Matt Wagantall49722712011-08-17 18:50:53 -0700191static int get_clocks(struct device *dev, struct footswitch *fs)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700192{
193 int rc;
194
195 /*
196 * Some SoCs may not have a separate rate-settable clock.
197 * If one can't be found, try to use the core clock for
198 * rate-setting instead.
199 */
Matt Wagantall49722712011-08-17 18:50:53 -0700200 if (fs->has_src_clk) {
201 fs->src_clk = clk_get(dev, "src_clk");
202 if (IS_ERR(fs->src_clk))
203 fs->src_clk = clk_get(dev, "core_clk");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700204 } else {
Matt Wagantall49722712011-08-17 18:50:53 -0700205 fs->src_clk = clk_get(dev, "core_clk");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700206 }
Matt Wagantall49722712011-08-17 18:50:53 -0700207 if (IS_ERR(fs->src_clk)) {
Matt Wagantall27fa2822012-02-22 18:43:27 -0800208 pr_err("%s clk_get(src_clk) failed\n", fs->desc.name);
Matt Wagantall49722712011-08-17 18:50:53 -0700209 rc = PTR_ERR(fs->src_clk);
210 goto err_src_clk;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700211 }
212
Matt Wagantall49722712011-08-17 18:50:53 -0700213 fs->core_clk = clk_get(dev, "core_clk");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700214 if (IS_ERR(fs->core_clk)) {
Matt Wagantall27fa2822012-02-22 18:43:27 -0800215 pr_err("%s clk_get(core_clk) failed\n", fs->desc.name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700216 rc = PTR_ERR(fs->core_clk);
217 goto err_core_clk;
218 }
219
Matt Wagantall49722712011-08-17 18:50:53 -0700220 if (fs->has_ahb_clk) {
221 fs->ahb_clk = clk_get(dev, "iface_clk");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700222 if (IS_ERR(fs->ahb_clk)) {
Matt Wagantall27fa2822012-02-22 18:43:27 -0800223 pr_err("%s clk_get(iface_clk) failed\n", fs->desc.name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700224 rc = PTR_ERR(fs->ahb_clk);
225 goto err_ahb_clk;
226 }
227 }
228
229 return 0;
230
231err_ahb_clk:
232 clk_put(fs->core_clk);
233err_core_clk:
Matt Wagantall49722712011-08-17 18:50:53 -0700234 clk_put(fs->src_clk);
235err_src_clk:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700236 return rc;
237}
238
239static void put_clocks(struct footswitch *fs)
240{
Matt Wagantall49722712011-08-17 18:50:53 -0700241 clk_put(fs->src_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700242 clk_put(fs->core_clk);
243 clk_put(fs->ahb_clk);
244}
245
246static int footswitch_probe(struct platform_device *pdev)
247{
248 struct footswitch *fs;
249 struct regulator_init_data *init_data;
250 int rc;
251
252 if (pdev == NULL)
253 return -EINVAL;
254
255 if (pdev->id >= MAX_FS)
256 return -ENODEV;
257
258 fs = &footswitches[pdev->id];
259 if (!fs->is_manual) {
260 pr_err("%s is not in manual mode\n", fs->desc.name);
261 return -EINVAL;
262 }
263 init_data = pdev->dev.platform_data;
264
Matt Wagantall49722712011-08-17 18:50:53 -0700265 rc = get_clocks(&pdev->dev, fs);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700266 if (rc)
267 return rc;
268
269 fs->rdev = regulator_register(&fs->desc, &pdev->dev, init_data, fs);
270 if (IS_ERR(fs->rdev)) {
271 pr_err("regulator_register(%s) failed\n", fs->desc.name);
272 rc = PTR_ERR(fs->rdev);
273 goto err_register;
274 }
275
276 return 0;
277
278err_register:
279 put_clocks(fs);
280
281 return rc;
282}
283
284static int __devexit footswitch_remove(struct platform_device *pdev)
285{
286 struct footswitch *fs = &footswitches[pdev->id];
287
288 regulator_unregister(fs->rdev);
289 set_rail_mode(fs->pcom_id, PCOM_RAIL_MODE_AUTO);
290 put_clocks(fs);
291
292 return 0;
293}
294
295static struct platform_driver footswitch_driver = {
296 .probe = footswitch_probe,
297 .remove = __devexit_p(footswitch_remove),
298 .driver = {
299 .name = "footswitch-pcom",
300 .owner = THIS_MODULE,
301 },
302};
303
304static int __init footswitch_init(void)
305{
306 struct footswitch *fs;
307 int ret;
308
309 /*
310 * Enable all footswitches in manual mode (ie. not controlled along
311 * with pcom clocks).
312 */
313 for (fs = footswitches; fs < footswitches + ARRAY_SIZE(footswitches);
314 fs++) {
315 set_rail_state(fs->pcom_id, PCOM_CLKCTL_RPC_RAIL_ENABLE);
316 ret = set_rail_mode(fs->pcom_id, PCOM_RAIL_MODE_MANUAL);
317 if (!ret)
318 fs->is_manual = 1;
319 }
320
321 return platform_driver_register(&footswitch_driver);
322}
323subsys_initcall(footswitch_init);
324
325static void __exit footswitch_exit(void)
326{
327 platform_driver_unregister(&footswitch_driver);
328}
329module_exit(footswitch_exit);
330
Matt Wagantalldf9d32a2011-09-09 13:50:06 -0700331MODULE_LICENSE("GPL v2");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700332MODULE_DESCRIPTION("proc_comm rail footswitch");
333MODULE_ALIAS("platform:footswitch-pcom");