blob: 07d711828636b6efc439f04397772ea4ab236e7e [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>
Steve Mucklef132c6c2012-06-06 18:30:57 -070021#include <linux/module.h>
Taniya Das78b72d62011-12-02 15:54:21 +053022#include <mach/socinfo.h>
Steve Mucklef132c6c2012-06-06 18:30:57 -070023#include <mach/proc_comm.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070024#include "footswitch.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070025
26/* PCOM power rail IDs */
27#define PCOM_FS_GRP 8
28#define PCOM_FS_GRP_2D 58
29#define PCOM_FS_MDP 14
30#define PCOM_FS_MFC 68
31#define PCOM_FS_ROTATOR 90
32#define PCOM_FS_VFE 41
33#define PCOM_FS_VPE 76
34
35#define PCOM_RAIL_MODE_AUTO 0
36#define PCOM_RAIL_MODE_MANUAL 1
37
38/**
39 * struct footswitch - Per-footswitch data and state
40 * @rdev: Regulator framework device
41 * @desc: Regulator descriptor
42 * @init_data: Regulator platform data
43 * @pcom_id: Proc-comm ID of the footswitch
44 * @is_enabled: Flag set when footswitch is enabled
45 * @is_manual: Flag set when footswitch is in manual proc-comm mode
Matt Wagantall49722712011-08-17 18:50:53 -070046 * @has_ahb_clk: Flag set if footswitched core has an ahb_clk
47 * @has_src_clk: Flag set if footswitched core has a src_clk
48 * @src_clk: Controls the core clock's rate
49 * @core_clk: Clocks the core
50 * @ahb_clk: Clocks the core's register interface
51 * @src_clk_init_rate: Rate to use for src_clk if it has not been set yet
52 * @is_rate_set: Flag set if core_clk's rate has been set
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070053 */
54struct footswitch {
55 struct regulator_dev *rdev;
56 struct regulator_desc desc;
57 struct regulator_init_data init_data;
58 unsigned pcom_id;
59 bool is_enabled;
60 bool is_manual;
Matt Wagantall49722712011-08-17 18:50:53 -070061 struct clk *src_clk;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070062 struct clk *core_clk;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070063 struct clk *ahb_clk;
Matt Wagantall49722712011-08-17 18:50:53 -070064 const bool has_ahb_clk;
65 const bool has_src_clk;
66 const int src_clk_init_rate;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070067 bool is_rate_set;
68};
69
70static inline int set_rail_mode(int pcom_id, int mode)
71{
72 int rc;
73
74 rc = msm_proc_comm(PCOM_CLKCTL_RPC_RAIL_CONTROL, &pcom_id, &mode);
75 if (!rc && pcom_id)
76 rc = -EINVAL;
77
78 return rc;
79}
80
81static inline int set_rail_state(int pcom_id, int state)
82{
83 int rc;
84
85 rc = msm_proc_comm(state, &pcom_id, NULL);
86 if (!rc && pcom_id)
87 rc = -EINVAL;
88
89 return rc;
90}
91
92static int enable_clocks(struct footswitch *fs)
93{
Matt Wagantall49722712011-08-17 18:50:53 -070094 fs->is_rate_set = !!(clk_get_rate(fs->src_clk));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070095 if (!fs->is_rate_set)
Matt Wagantall49722712011-08-17 18:50:53 -070096 clk_set_rate(fs->src_clk, fs->src_clk_init_rate);
Matt Wagantall1a7ee892012-01-17 18:56:28 -080097 clk_prepare_enable(fs->core_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070098
99 if (fs->ahb_clk)
Matt Wagantall1a7ee892012-01-17 18:56:28 -0800100 clk_prepare_enable(fs->ahb_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700101
102 return 0;
103}
104
105static void disable_clocks(struct footswitch *fs)
106{
107 if (fs->ahb_clk)
Matt Wagantall1a7ee892012-01-17 18:56:28 -0800108 clk_disable_unprepare(fs->ahb_clk);
109 clk_disable_unprepare(fs->core_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700110}
111
112static int footswitch_is_enabled(struct regulator_dev *rdev)
113{
114 struct footswitch *fs = rdev_get_drvdata(rdev);
115
116 return fs->is_enabled;
117}
118
119static int footswitch_enable(struct regulator_dev *rdev)
120{
121 struct footswitch *fs = rdev_get_drvdata(rdev);
122 int rc;
123
124 rc = enable_clocks(fs);
125 if (rc)
126 return rc;
127
128 rc = set_rail_state(fs->pcom_id, PCOM_CLKCTL_RPC_RAIL_ENABLE);
129 if (!rc)
130 fs->is_enabled = true;
131
132 disable_clocks(fs);
133
134 return rc;
135}
136
137static int footswitch_disable(struct regulator_dev *rdev)
138{
139 struct footswitch *fs = rdev_get_drvdata(rdev);
140 int rc;
141
142 rc = enable_clocks(fs);
143 if (rc)
144 return rc;
145
146 rc = set_rail_state(fs->pcom_id, PCOM_CLKCTL_RPC_RAIL_DISABLE);
147 if (!rc)
148 fs->is_enabled = false;
149
150 disable_clocks(fs);
151
152 return rc;
153}
154
155static struct regulator_ops footswitch_ops = {
156 .is_enabled = footswitch_is_enabled,
157 .enable = footswitch_enable,
158 .disable = footswitch_disable,
159};
160
Matt Wagantall49722712011-08-17 18:50:53 -0700161#define FOOTSWITCH(_id, _pcom_id, _name, _src_clk, _rate, _ahb_clk) \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700162 [_id] = { \
163 .desc = { \
164 .id = _id, \
165 .name = _name, \
166 .ops = &footswitch_ops, \
167 .type = REGULATOR_VOLTAGE, \
168 .owner = THIS_MODULE, \
169 }, \
170 .pcom_id = _pcom_id, \
Matt Wagantall49722712011-08-17 18:50:53 -0700171 .has_src_clk = _src_clk, \
172 .src_clk_init_rate = _rate, \
173 .has_ahb_clk = _ahb_clk, \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700174 }
175static struct footswitch footswitches[] = {
Matt Wagantall49722712011-08-17 18:50:53 -0700176 FOOTSWITCH(FS_GFX3D, PCOM_FS_GRP,
177 "fs_gfx3d", true, 24576000, true),
178 FOOTSWITCH(FS_GFX2D0, PCOM_FS_GRP_2D,
179 "fs_gfx2d0", false, 24576000, true),
180 FOOTSWITCH(FS_MDP, PCOM_FS_MDP,
181 "fs_mdp", false, 24576000, true),
182 FOOTSWITCH(FS_MFC, PCOM_FS_MFC,
183 "fs_mfc", false, 24576000, true),
184 FOOTSWITCH(FS_ROT, PCOM_FS_ROTATOR,
185 "fs_rot", false, 0, true),
186 FOOTSWITCH(FS_VFE, PCOM_FS_VFE,
187 "fs_vfe", false, 24576000, true),
188 FOOTSWITCH(FS_VPE, PCOM_FS_VPE,
189 "fs_vpe", false, 24576000, false),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700190};
191
Matt Wagantall49722712011-08-17 18:50:53 -0700192static int get_clocks(struct device *dev, struct footswitch *fs)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700193{
194 int rc;
195
196 /*
197 * Some SoCs may not have a separate rate-settable clock.
198 * If one can't be found, try to use the core clock for
199 * rate-setting instead.
200 */
Matt Wagantall49722712011-08-17 18:50:53 -0700201 if (fs->has_src_clk) {
202 fs->src_clk = clk_get(dev, "src_clk");
203 if (IS_ERR(fs->src_clk))
204 fs->src_clk = clk_get(dev, "core_clk");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700205 } else {
Matt Wagantall49722712011-08-17 18:50:53 -0700206 fs->src_clk = clk_get(dev, "core_clk");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700207 }
Matt Wagantall49722712011-08-17 18:50:53 -0700208 if (IS_ERR(fs->src_clk)) {
Matt Wagantall27fa2822012-02-22 18:43:27 -0800209 pr_err("%s clk_get(src_clk) failed\n", fs->desc.name);
Matt Wagantall49722712011-08-17 18:50:53 -0700210 rc = PTR_ERR(fs->src_clk);
211 goto err_src_clk;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700212 }
213
Matt Wagantall49722712011-08-17 18:50:53 -0700214 fs->core_clk = clk_get(dev, "core_clk");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700215 if (IS_ERR(fs->core_clk)) {
Matt Wagantall27fa2822012-02-22 18:43:27 -0800216 pr_err("%s clk_get(core_clk) failed\n", fs->desc.name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700217 rc = PTR_ERR(fs->core_clk);
218 goto err_core_clk;
219 }
220
Matt Wagantall49722712011-08-17 18:50:53 -0700221 if (fs->has_ahb_clk) {
222 fs->ahb_clk = clk_get(dev, "iface_clk");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700223 if (IS_ERR(fs->ahb_clk)) {
Matt Wagantall27fa2822012-02-22 18:43:27 -0800224 pr_err("%s clk_get(iface_clk) failed\n", fs->desc.name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700225 rc = PTR_ERR(fs->ahb_clk);
226 goto err_ahb_clk;
227 }
228 }
229
230 return 0;
231
232err_ahb_clk:
233 clk_put(fs->core_clk);
234err_core_clk:
Matt Wagantall49722712011-08-17 18:50:53 -0700235 clk_put(fs->src_clk);
236err_src_clk:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700237 return rc;
238}
239
240static void put_clocks(struct footswitch *fs)
241{
Matt Wagantall49722712011-08-17 18:50:53 -0700242 clk_put(fs->src_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700243 clk_put(fs->core_clk);
244 clk_put(fs->ahb_clk);
245}
246
247static int footswitch_probe(struct platform_device *pdev)
248{
249 struct footswitch *fs;
250 struct regulator_init_data *init_data;
251 int rc;
252
253 if (pdev == NULL)
254 return -EINVAL;
255
256 if (pdev->id >= MAX_FS)
257 return -ENODEV;
258
259 fs = &footswitches[pdev->id];
260 if (!fs->is_manual) {
261 pr_err("%s is not in manual mode\n", fs->desc.name);
262 return -EINVAL;
263 }
264 init_data = pdev->dev.platform_data;
265
Matt Wagantall49722712011-08-17 18:50:53 -0700266 rc = get_clocks(&pdev->dev, fs);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700267 if (rc)
268 return rc;
269
Rajendra Nayak11eafc62011-11-18 16:47:19 +0530270 fs->rdev = regulator_register(&fs->desc, &pdev->dev,
271 init_data, fs, NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700272 if (IS_ERR(fs->rdev)) {
273 pr_err("regulator_register(%s) failed\n", fs->desc.name);
274 rc = PTR_ERR(fs->rdev);
275 goto err_register;
276 }
277
278 return 0;
279
280err_register:
281 put_clocks(fs);
282
283 return rc;
284}
285
286static int __devexit footswitch_remove(struct platform_device *pdev)
287{
288 struct footswitch *fs = &footswitches[pdev->id];
289
290 regulator_unregister(fs->rdev);
291 set_rail_mode(fs->pcom_id, PCOM_RAIL_MODE_AUTO);
292 put_clocks(fs);
293
294 return 0;
295}
296
297static struct platform_driver footswitch_driver = {
298 .probe = footswitch_probe,
299 .remove = __devexit_p(footswitch_remove),
300 .driver = {
301 .name = "footswitch-pcom",
302 .owner = THIS_MODULE,
303 },
304};
305
306static int __init footswitch_init(void)
307{
308 struct footswitch *fs;
309 int ret;
310
311 /*
312 * Enable all footswitches in manual mode (ie. not controlled along
313 * with pcom clocks).
314 */
315 for (fs = footswitches; fs < footswitches + ARRAY_SIZE(footswitches);
316 fs++) {
317 set_rail_state(fs->pcom_id, PCOM_CLKCTL_RPC_RAIL_ENABLE);
318 ret = set_rail_mode(fs->pcom_id, PCOM_RAIL_MODE_MANUAL);
319 if (!ret)
320 fs->is_manual = 1;
321 }
322
323 return platform_driver_register(&footswitch_driver);
324}
325subsys_initcall(footswitch_init);
326
327static void __exit footswitch_exit(void)
328{
329 platform_driver_unregister(&footswitch_driver);
330}
331module_exit(footswitch_exit);
332
Matt Wagantalldf9d32a2011-09-09 13:50:06 -0700333MODULE_LICENSE("GPL v2");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700334MODULE_DESCRIPTION("proc_comm rail footswitch");
335MODULE_ALIAS("platform:footswitch-pcom");