blob: ae1e27395eda594e8d9fa4dc40febc24946a772e [file] [log] [blame]
Duy Truong790f06d2013-02-13 16:38:12 -08001/* Copyright (c) 2010-2011, The Linux Foundation. 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/err.h>
16#include <linux/kernel.h>
Steve Mucklef132c6c2012-06-06 18:30:57 -070017#include <linux/module.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070018#include <linux/init.h>
19#include <linux/delay.h>
20#include <linux/slab.h>
21#include <linux/string.h>
22#include <linux/platform_device.h>
23#include <linux/regulator/driver.h>
24#include <linux/regulator/machine.h>
25#include <linux/mfd/pmic8901.h>
26
27#include "spm.h"
28
29#define FTSMPS_VCTRL_BAND_MASK 0xC0
30#define FTSMPS_VCTRL_BAND_1 0x40
31#define FTSMPS_VCTRL_BAND_2 0x80
32#define FTSMPS_VCTRL_BAND_3 0xC0
33#define FTSMPS_VCTRL_VPROG_MASK 0x3F
34
35#define FTSMPS_BAND1_UV_MIN 350000
36#define FTSMPS_BAND1_UV_MAX 650000
37/* 3 LSB's of program voltage must be 0 in band 1. */
38/* Logical step size */
39#define FTSMPS_BAND1_UV_LOG_STEP 50000
40/* Physical step size */
41#define FTSMPS_BAND1_UV_PHYS_STEP 6250
42
43#define FTSMPS_BAND2_UV_MIN 700000
44#define FTSMPS_BAND2_UV_MAX 1400000
45#define FTSMPS_BAND2_UV_STEP 12500
46
47#define FTSMPS_BAND3_UV_MIN 1400000
48#define FTSMPS_BAND3_UV_SET_POINT_MIN 1500000
49#define FTSMPS_BAND3_UV_MAX 3300000
50#define FTSMPS_BAND3_UV_STEP 50000
51
52struct saw_vreg {
53 struct regulator_desc desc;
54 struct regulator_dev *rdev;
55 char *name;
56 int uV;
Michael Bohanee3ce19c2012-11-13 14:57:40 -080057 int last_set_uV;
58 unsigned vlevel;
59 bool online;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070060};
61
62/* Minimum core operating voltage */
63#define MIN_CORE_VOLTAGE 950000
64
Michael Bohanee3ce19c2012-11-13 14:57:40 -080065/* Specifies an uninitialized voltage */
66#define INVALID_VOLTAGE -1
67
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070068/* Specifies the PMIC internal slew rate in uV/us. */
69#define REGULATOR_SLEW_RATE 1250
70
71static int saw_get_voltage(struct regulator_dev *rdev)
72{
73 struct saw_vreg *vreg = rdev_get_drvdata(rdev);
74
75 return vreg->uV;
76}
77
Michael Bohanee3ce19c2012-11-13 14:57:40 -080078static int _set_voltage(struct regulator_dev *rdev)
79{
80 struct saw_vreg *vreg = rdev_get_drvdata(rdev);
81 int rc;
82
83 rc = msm_spm_set_vdd(rdev_get_id(rdev), vreg->vlevel);
84 if (!rc) {
85 if (vreg->uV > vreg->last_set_uV) {
86 /* Wait for voltage to stabalize. */
87 udelay((vreg->uV - vreg->last_set_uV) /
88 REGULATOR_SLEW_RATE);
89 }
90 vreg->last_set_uV = vreg->uV;
91 } else {
92 pr_err("%s: msm_spm_set_vdd failed %d\n", vreg->name, rc);
93 vreg->uV = vreg->last_set_uV;
94 }
95
96 return rc;
97}
98
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070099static int saw_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV,
100 unsigned *selector)
101{
102 struct saw_vreg *vreg = rdev_get_drvdata(rdev);
103 int uV = min_uV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700104 u8 vprog, band;
105
106 if (uV < FTSMPS_BAND1_UV_MIN && max_uV >= FTSMPS_BAND1_UV_MIN)
107 uV = FTSMPS_BAND1_UV_MIN;
108
109 if (uV < FTSMPS_BAND1_UV_MIN || uV > FTSMPS_BAND3_UV_MAX) {
110 pr_err("%s: request v=[%d, %d] is outside possible "
111 "v=[%d, %d]\n", vreg->name, min_uV, max_uV,
112 FTSMPS_BAND1_UV_MIN, FTSMPS_BAND3_UV_MAX);
113 return -EINVAL;
114 }
115
116 /* Round up for set points in the gaps between bands. */
117 if (uV > FTSMPS_BAND1_UV_MAX && uV < FTSMPS_BAND2_UV_MIN)
118 uV = FTSMPS_BAND2_UV_MIN;
119 else if (uV > FTSMPS_BAND2_UV_MAX
120 && uV < FTSMPS_BAND3_UV_SET_POINT_MIN)
121 uV = FTSMPS_BAND3_UV_SET_POINT_MIN;
122
123 if (uV > FTSMPS_BAND2_UV_MAX) {
124 vprog = (uV - FTSMPS_BAND3_UV_MIN + FTSMPS_BAND3_UV_STEP - 1)
125 / FTSMPS_BAND3_UV_STEP;
126 band = FTSMPS_VCTRL_BAND_3;
127 uV = FTSMPS_BAND3_UV_MIN + vprog * FTSMPS_BAND3_UV_STEP;
128 } else if (uV > FTSMPS_BAND1_UV_MAX) {
129 vprog = (uV - FTSMPS_BAND2_UV_MIN + FTSMPS_BAND2_UV_STEP - 1)
130 / FTSMPS_BAND2_UV_STEP;
131 band = FTSMPS_VCTRL_BAND_2;
132 uV = FTSMPS_BAND2_UV_MIN + vprog * FTSMPS_BAND2_UV_STEP;
133 } else {
134 vprog = (uV - FTSMPS_BAND1_UV_MIN
135 + FTSMPS_BAND1_UV_LOG_STEP - 1)
136 / FTSMPS_BAND1_UV_LOG_STEP;
137 uV = FTSMPS_BAND1_UV_MIN + vprog * FTSMPS_BAND1_UV_LOG_STEP;
138 vprog *= FTSMPS_BAND1_UV_LOG_STEP / FTSMPS_BAND1_UV_PHYS_STEP;
139 band = FTSMPS_VCTRL_BAND_1;
140 }
141
142 if (uV > max_uV) {
143 pr_err("%s: request v=[%d, %d] cannot be met by any setpoint\n",
144 vreg->name, min_uV, max_uV);
145 return -EINVAL;
146 }
147
Michael Bohanee3ce19c2012-11-13 14:57:40 -0800148 vreg->vlevel = band | vprog;
149 vreg->uV = uV;
150
151 if (!vreg->online)
152 return 0;
153
154 return _set_voltage(rdev);
155}
156
157static int saw_enable(struct regulator_dev *rdev)
158{
159 struct saw_vreg *vreg = rdev_get_drvdata(rdev);
160 int rc = 0;
161
162 if (vreg->uV != vreg->last_set_uV)
163 rc = _set_voltage(rdev);
164
165 if (!rc)
166 vreg->online = true;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700167
168 return rc;
169}
170
Michael Bohanee3ce19c2012-11-13 14:57:40 -0800171static int saw_disable(struct regulator_dev *rdev)
172{
173 struct saw_vreg *vreg = rdev_get_drvdata(rdev);
174
175 vreg->online = false;
176
177 return 0;
178}
179
180static int saw_is_enabled(struct regulator_dev *rdev)
181{
182 struct saw_vreg *vreg = rdev_get_drvdata(rdev);
183
184 return vreg->online;
185}
186
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700187static struct regulator_ops saw_ops = {
188 .get_voltage = saw_get_voltage,
189 .set_voltage = saw_set_voltage,
Michael Bohanee3ce19c2012-11-13 14:57:40 -0800190 .enable = saw_enable,
191 .disable = saw_disable,
192 .is_enabled = saw_is_enabled,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700193};
194
195static int __devinit saw_probe(struct platform_device *pdev)
196{
197 struct regulator_init_data *init_data;
198 struct saw_vreg *vreg;
199 int rc = 0;
200
201 if (!pdev->dev.platform_data) {
202 pr_err("platform data required.\n");
203 return -EINVAL;
204 }
205
206 init_data = pdev->dev.platform_data;
207 if (!init_data->constraints.name) {
208 pr_err("regulator name must be specified in constraints.\n");
209 return -EINVAL;
210 }
211
212 vreg = kzalloc(sizeof(struct saw_vreg), GFP_KERNEL);
213 if (!vreg) {
214 pr_err("kzalloc failed.\n");
215 return -ENOMEM;
216 }
217
218 vreg->name = kstrdup(init_data->constraints.name, GFP_KERNEL);
219 if (!vreg->name) {
220 pr_err("kzalloc failed.\n");
221 rc = -ENOMEM;
222 goto free_vreg;
223 }
224
Michael Bohanee3ce19c2012-11-13 14:57:40 -0800225 vreg->desc.name = vreg->name;
226 vreg->desc.id = pdev->id;
227 vreg->desc.ops = &saw_ops;
228 vreg->desc.type = REGULATOR_VOLTAGE;
229 vreg->desc.owner = THIS_MODULE;
230 vreg->uV = INVALID_VOLTAGE;
231 vreg->last_set_uV = MIN_CORE_VOLTAGE;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700232
Rajendra Nayak11eafc62011-11-18 16:47:19 +0530233 vreg->rdev = regulator_register(&vreg->desc, &pdev->dev,
234 init_data, vreg, NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700235 if (IS_ERR(vreg->rdev)) {
236 rc = PTR_ERR(vreg->rdev);
237 pr_err("regulator_register failed, rc=%d.\n", rc);
238 goto free_name;
239 }
240
241 platform_set_drvdata(pdev, vreg);
242
243 pr_info("id=%d, name=%s\n", pdev->id, vreg->name);
244
245 return rc;
246
247free_name:
248 kfree(vreg->name);
249free_vreg:
250 kfree(vreg);
251
252 return rc;
253}
254
255static int __devexit saw_remove(struct platform_device *pdev)
256{
257 struct saw_vreg *vreg = platform_get_drvdata(pdev);
258
259 regulator_unregister(vreg->rdev);
260 kfree(vreg->name);
261 kfree(vreg);
262 platform_set_drvdata(pdev, NULL);
263
264 return 0;
265}
266
267static struct platform_driver saw_driver = {
268 .probe = saw_probe,
269 .remove = __devexit_p(saw_remove),
270 .driver = {
271 .name = "saw-regulator",
272 .owner = THIS_MODULE,
273 },
274};
275
276static int __init saw_init(void)
277{
278 return platform_driver_register(&saw_driver);
279}
280
281static void __exit saw_exit(void)
282{
283 platform_driver_unregister(&saw_driver);
284}
285
286postcore_initcall(saw_init);
287module_exit(saw_exit);
288
289MODULE_LICENSE("GPL v2");
290MODULE_DESCRIPTION("SAW regulator driver");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700291MODULE_ALIAS("platform:saw-regulator");