blob: b11b1fa2caef69e58dc45619ce21115edebc03e0 [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#define pr_fmt(fmt) "%s: " fmt, __func__
14
15#include <linux/err.h>
16#include <linux/kernel.h>
17#include <linux/init.h>
18#include <linux/delay.h>
19#include <linux/slab.h>
20#include <linux/string.h>
21#include <linux/platform_device.h>
22#include <linux/regulator/driver.h>
23#include <linux/regulator/machine.h>
24#include <linux/mfd/pmic8901.h>
25
26#include "spm.h"
27
28#define FTSMPS_VCTRL_BAND_MASK 0xC0
29#define FTSMPS_VCTRL_BAND_1 0x40
30#define FTSMPS_VCTRL_BAND_2 0x80
31#define FTSMPS_VCTRL_BAND_3 0xC0
32#define FTSMPS_VCTRL_VPROG_MASK 0x3F
33
34#define FTSMPS_BAND1_UV_MIN 350000
35#define FTSMPS_BAND1_UV_MAX 650000
36/* 3 LSB's of program voltage must be 0 in band 1. */
37/* Logical step size */
38#define FTSMPS_BAND1_UV_LOG_STEP 50000
39/* Physical step size */
40#define FTSMPS_BAND1_UV_PHYS_STEP 6250
41
42#define FTSMPS_BAND2_UV_MIN 700000
43#define FTSMPS_BAND2_UV_MAX 1400000
44#define FTSMPS_BAND2_UV_STEP 12500
45
46#define FTSMPS_BAND3_UV_MIN 1400000
47#define FTSMPS_BAND3_UV_SET_POINT_MIN 1500000
48#define FTSMPS_BAND3_UV_MAX 3300000
49#define FTSMPS_BAND3_UV_STEP 50000
50
51struct saw_vreg {
52 struct regulator_desc desc;
53 struct regulator_dev *rdev;
54 char *name;
55 int uV;
56};
57
58/* Minimum core operating voltage */
59#define MIN_CORE_VOLTAGE 950000
60
61/* Specifies the PMIC internal slew rate in uV/us. */
62#define REGULATOR_SLEW_RATE 1250
63
64static int saw_get_voltage(struct regulator_dev *rdev)
65{
66 struct saw_vreg *vreg = rdev_get_drvdata(rdev);
67
68 return vreg->uV;
69}
70
71static int saw_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV,
72 unsigned *selector)
73{
74 struct saw_vreg *vreg = rdev_get_drvdata(rdev);
75 int uV = min_uV;
76 int rc;
77 u8 vprog, band;
78
79 if (uV < FTSMPS_BAND1_UV_MIN && max_uV >= FTSMPS_BAND1_UV_MIN)
80 uV = FTSMPS_BAND1_UV_MIN;
81
82 if (uV < FTSMPS_BAND1_UV_MIN || uV > FTSMPS_BAND3_UV_MAX) {
83 pr_err("%s: request v=[%d, %d] is outside possible "
84 "v=[%d, %d]\n", vreg->name, min_uV, max_uV,
85 FTSMPS_BAND1_UV_MIN, FTSMPS_BAND3_UV_MAX);
86 return -EINVAL;
87 }
88
89 /* Round up for set points in the gaps between bands. */
90 if (uV > FTSMPS_BAND1_UV_MAX && uV < FTSMPS_BAND2_UV_MIN)
91 uV = FTSMPS_BAND2_UV_MIN;
92 else if (uV > FTSMPS_BAND2_UV_MAX
93 && uV < FTSMPS_BAND3_UV_SET_POINT_MIN)
94 uV = FTSMPS_BAND3_UV_SET_POINT_MIN;
95
96 if (uV > FTSMPS_BAND2_UV_MAX) {
97 vprog = (uV - FTSMPS_BAND3_UV_MIN + FTSMPS_BAND3_UV_STEP - 1)
98 / FTSMPS_BAND3_UV_STEP;
99 band = FTSMPS_VCTRL_BAND_3;
100 uV = FTSMPS_BAND3_UV_MIN + vprog * FTSMPS_BAND3_UV_STEP;
101 } else if (uV > FTSMPS_BAND1_UV_MAX) {
102 vprog = (uV - FTSMPS_BAND2_UV_MIN + FTSMPS_BAND2_UV_STEP - 1)
103 / FTSMPS_BAND2_UV_STEP;
104 band = FTSMPS_VCTRL_BAND_2;
105 uV = FTSMPS_BAND2_UV_MIN + vprog * FTSMPS_BAND2_UV_STEP;
106 } else {
107 vprog = (uV - FTSMPS_BAND1_UV_MIN
108 + FTSMPS_BAND1_UV_LOG_STEP - 1)
109 / FTSMPS_BAND1_UV_LOG_STEP;
110 uV = FTSMPS_BAND1_UV_MIN + vprog * FTSMPS_BAND1_UV_LOG_STEP;
111 vprog *= FTSMPS_BAND1_UV_LOG_STEP / FTSMPS_BAND1_UV_PHYS_STEP;
112 band = FTSMPS_VCTRL_BAND_1;
113 }
114
115 if (uV > max_uV) {
116 pr_err("%s: request v=[%d, %d] cannot be met by any setpoint\n",
117 vreg->name, min_uV, max_uV);
118 return -EINVAL;
119 }
120
121 rc = msm_spm_set_vdd(rdev_get_id(rdev), band | vprog);
122 if (!rc) {
123 if (uV > vreg->uV) {
124 /* Wait for voltage to stabalize. */
125 udelay((uV - vreg->uV) / REGULATOR_SLEW_RATE);
126 }
127 vreg->uV = uV;
128 } else {
129 pr_err("%s: msm_spm_set_vdd failed %d\n", vreg->name, rc);
130 }
131
132 return rc;
133}
134
135static struct regulator_ops saw_ops = {
136 .get_voltage = saw_get_voltage,
137 .set_voltage = saw_set_voltage,
138};
139
140static int __devinit saw_probe(struct platform_device *pdev)
141{
142 struct regulator_init_data *init_data;
143 struct saw_vreg *vreg;
144 int rc = 0;
145
146 if (!pdev->dev.platform_data) {
147 pr_err("platform data required.\n");
148 return -EINVAL;
149 }
150
151 init_data = pdev->dev.platform_data;
152 if (!init_data->constraints.name) {
153 pr_err("regulator name must be specified in constraints.\n");
154 return -EINVAL;
155 }
156
157 vreg = kzalloc(sizeof(struct saw_vreg), GFP_KERNEL);
158 if (!vreg) {
159 pr_err("kzalloc failed.\n");
160 return -ENOMEM;
161 }
162
163 vreg->name = kstrdup(init_data->constraints.name, GFP_KERNEL);
164 if (!vreg->name) {
165 pr_err("kzalloc failed.\n");
166 rc = -ENOMEM;
167 goto free_vreg;
168 }
169
170 vreg->desc.name = vreg->name;
171 vreg->desc.id = pdev->id;
172 vreg->desc.ops = &saw_ops;
173 vreg->desc.type = REGULATOR_VOLTAGE;
174 vreg->desc.owner = THIS_MODULE;
175 vreg->uV = MIN_CORE_VOLTAGE;
176
177 vreg->rdev = regulator_register(&vreg->desc, &pdev->dev, init_data,
178 vreg);
179 if (IS_ERR(vreg->rdev)) {
180 rc = PTR_ERR(vreg->rdev);
181 pr_err("regulator_register failed, rc=%d.\n", rc);
182 goto free_name;
183 }
184
185 platform_set_drvdata(pdev, vreg);
186
187 pr_info("id=%d, name=%s\n", pdev->id, vreg->name);
188
189 return rc;
190
191free_name:
192 kfree(vreg->name);
193free_vreg:
194 kfree(vreg);
195
196 return rc;
197}
198
199static int __devexit saw_remove(struct platform_device *pdev)
200{
201 struct saw_vreg *vreg = platform_get_drvdata(pdev);
202
203 regulator_unregister(vreg->rdev);
204 kfree(vreg->name);
205 kfree(vreg);
206 platform_set_drvdata(pdev, NULL);
207
208 return 0;
209}
210
211static struct platform_driver saw_driver = {
212 .probe = saw_probe,
213 .remove = __devexit_p(saw_remove),
214 .driver = {
215 .name = "saw-regulator",
216 .owner = THIS_MODULE,
217 },
218};
219
220static int __init saw_init(void)
221{
222 return platform_driver_register(&saw_driver);
223}
224
225static void __exit saw_exit(void)
226{
227 platform_driver_unregister(&saw_driver);
228}
229
230postcore_initcall(saw_init);
231module_exit(saw_exit);
232
233MODULE_LICENSE("GPL v2");
234MODULE_DESCRIPTION("SAW regulator driver");
235MODULE_VERSION("1.0");
236MODULE_ALIAS("platform:saw-regulator");