diff options
author | MyungJoo Ham <myungjoo.ham@samsung.com> | 2010-08-20 01:43:56 -0400 |
---|---|---|
committer | Liam Girdwood <lrg@slimlogic.co.uk> | 2010-10-28 17:40:31 -0400 |
commit | 202f4f53e503ae09b431459131b5b3a99fa6d839 (patch) | |
tree | 71f4597427f8bc9011aca93f873bd01154f2d25d /drivers/regulator/max8952.c | |
parent | 2d3b07c07b39c4b7d9b6641052a02d996cd5d87c (diff) |
MAX8952 PMIC Driver Initial Release
MAX8952 PMIC is used to provide voltage output between 770mV - 1400mV
with DVS support. In this initial release, users can set voltages for
four DVS modes, RAMP delay values, and SYNC frequency.
Controlling FPWM/SYNC_MODE/Pull-Down/Ramp Modes and reading CHIP_ID
is not supported in this release.
If GPIO of EN is not valid in platform data, the driver assumes that it
is always-on. If GPIO of VID0 or VID1 is invalid, the driver pulls down
VID0 and VID1 to fix DVS mode as 0 and disables DVS support.
We assume that V_OUT is capable to provide every voltage from 770mV to
1.40V in 10mV steps although the data sheet has some ambiguity on it.
Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
--
v2:
- Style correction
- Can accept platform_data with invalid GPIOs
- Removed unnecessary features
- Improved error handling
Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
Diffstat (limited to 'drivers/regulator/max8952.c')
-rw-r--r-- | drivers/regulator/max8952.c | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/drivers/regulator/max8952.c b/drivers/regulator/max8952.c new file mode 100644 index 000000000000..f2af0b1c3925 --- /dev/null +++ b/drivers/regulator/max8952.c | |||
@@ -0,0 +1,360 @@ | |||
1 | /* | ||
2 | * max8952.c - Voltage and current regulation for the Maxim 8952 | ||
3 | * | ||
4 | * Copyright (C) 2010 Samsung Electronics | ||
5 | * MyungJoo Ham <myungjoo.ham@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | */ | ||
21 | |||
22 | #include <linux/module.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/i2c.h> | ||
25 | #include <linux/err.h> | ||
26 | #include <linux/platform_device.h> | ||
27 | #include <linux/regulator/driver.h> | ||
28 | #include <linux/regulator/max8952.h> | ||
29 | #include <linux/mutex.h> | ||
30 | #include <linux/gpio.h> | ||
31 | #include <linux/io.h> | ||
32 | #include <linux/slab.h> | ||
33 | |||
34 | /* Registers */ | ||
35 | enum { | ||
36 | MAX8952_REG_MODE0, | ||
37 | MAX8952_REG_MODE1, | ||
38 | MAX8952_REG_MODE2, | ||
39 | MAX8952_REG_MODE3, | ||
40 | MAX8952_REG_CONTROL, | ||
41 | MAX8952_REG_SYNC, | ||
42 | MAX8952_REG_RAMP, | ||
43 | MAX8952_REG_CHIP_ID1, | ||
44 | MAX8952_REG_CHIP_ID2, | ||
45 | }; | ||
46 | |||
47 | struct max8952_data { | ||
48 | struct i2c_client *client; | ||
49 | struct device *dev; | ||
50 | struct mutex mutex; | ||
51 | struct max8952_platform_data *pdata; | ||
52 | struct regulator_dev *rdev; | ||
53 | |||
54 | bool vid0; | ||
55 | bool vid1; | ||
56 | bool en; | ||
57 | }; | ||
58 | |||
59 | static int max8952_read_reg(struct max8952_data *max8952, u8 reg) | ||
60 | { | ||
61 | int ret = i2c_smbus_read_byte_data(max8952->client, reg); | ||
62 | if (ret > 0) | ||
63 | ret &= 0xff; | ||
64 | |||
65 | return ret; | ||
66 | } | ||
67 | |||
68 | static int max8952_write_reg(struct max8952_data *max8952, | ||
69 | u8 reg, u8 value) | ||
70 | { | ||
71 | return i2c_smbus_write_byte_data(max8952->client, reg, value); | ||
72 | } | ||
73 | |||
74 | static int max8952_voltage(struct max8952_data *max8952, u8 mode) | ||
75 | { | ||
76 | return (max8952->pdata->dvs_mode[mode] * 10 + 770) * 1000; | ||
77 | } | ||
78 | |||
79 | static int max8952_list_voltage(struct regulator_dev *rdev, | ||
80 | unsigned int selector) | ||
81 | { | ||
82 | struct max8952_data *max8952 = rdev_get_drvdata(rdev); | ||
83 | |||
84 | if (rdev_get_id(rdev) != 0) | ||
85 | return -EINVAL; | ||
86 | |||
87 | return max8952_voltage(max8952, selector); | ||
88 | } | ||
89 | |||
90 | static int max8952_is_enabled(struct regulator_dev *rdev) | ||
91 | { | ||
92 | struct max8952_data *max8952 = rdev_get_drvdata(rdev); | ||
93 | return max8952->en; | ||
94 | } | ||
95 | |||
96 | static int max8952_enable(struct regulator_dev *rdev) | ||
97 | { | ||
98 | struct max8952_data *max8952 = rdev_get_drvdata(rdev); | ||
99 | |||
100 | /* If not valid, assume "ALWAYS_HIGH" */ | ||
101 | if (gpio_is_valid(max8952->pdata->gpio_en)) | ||
102 | gpio_set_value(max8952->pdata->gpio_en, 1); | ||
103 | |||
104 | max8952->en = true; | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | static int max8952_disable(struct regulator_dev *rdev) | ||
109 | { | ||
110 | struct max8952_data *max8952 = rdev_get_drvdata(rdev); | ||
111 | |||
112 | /* If not valid, assume "ALWAYS_HIGH" -> not permitted */ | ||
113 | if (gpio_is_valid(max8952->pdata->gpio_en)) | ||
114 | gpio_set_value(max8952->pdata->gpio_en, 0); | ||
115 | else | ||
116 | return -EPERM; | ||
117 | |||
118 | max8952->en = false; | ||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | static int max8952_get_voltage(struct regulator_dev *rdev) | ||
123 | { | ||
124 | struct max8952_data *max8952 = rdev_get_drvdata(rdev); | ||
125 | u8 vid = 0; | ||
126 | |||
127 | if (max8952->vid0) | ||
128 | vid += 1; | ||
129 | if (max8952->vid1) | ||
130 | vid += 2; | ||
131 | |||
132 | return max8952_voltage(max8952, vid); | ||
133 | } | ||
134 | |||
135 | static int max8952_set_voltage(struct regulator_dev *rdev, | ||
136 | int min_uV, int max_uV) | ||
137 | { | ||
138 | struct max8952_data *max8952 = rdev_get_drvdata(rdev); | ||
139 | u8 vid = -1, i; | ||
140 | |||
141 | if (!gpio_is_valid(max8952->pdata->gpio_vid0) || | ||
142 | !gpio_is_valid(max8952->pdata->gpio_vid0)) { | ||
143 | /* DVS not supported */ | ||
144 | return -EPERM; | ||
145 | } | ||
146 | |||
147 | for (i = 0; i < MAX8952_NUM_DVS_MODE; i++) { | ||
148 | int volt = max8952_voltage(max8952, i); | ||
149 | |||
150 | /* Set the voltage as low as possible within the range */ | ||
151 | if (volt <= max_uV && volt >= min_uV) | ||
152 | if (vid == -1 || max8952_voltage(max8952, vid) > volt) | ||
153 | vid = i; | ||
154 | } | ||
155 | |||
156 | if (vid >= 0 && vid < MAX8952_NUM_DVS_MODE) { | ||
157 | max8952->vid0 = (vid % 2 == 1); | ||
158 | max8952->vid1 = (((vid >> 1) % 2) == 1); | ||
159 | gpio_set_value(max8952->pdata->gpio_vid0, max8952->vid0); | ||
160 | gpio_set_value(max8952->pdata->gpio_vid1, max8952->vid1); | ||
161 | } else | ||
162 | return -EINVAL; | ||
163 | |||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | static struct regulator_ops max8952_ops = { | ||
168 | .list_voltage = max8952_list_voltage, | ||
169 | .is_enabled = max8952_is_enabled, | ||
170 | .enable = max8952_enable, | ||
171 | .disable = max8952_disable, | ||
172 | .get_voltage = max8952_get_voltage, | ||
173 | .set_voltage = max8952_set_voltage, | ||
174 | .set_suspend_disable = max8952_disable, | ||
175 | }; | ||
176 | |||
177 | static struct regulator_desc regulator = { | ||
178 | .name = "MAX8952_VOUT", | ||
179 | .id = 0, | ||
180 | .n_voltages = MAX8952_NUM_DVS_MODE, | ||
181 | .ops = &max8952_ops, | ||
182 | .type = REGULATOR_VOLTAGE, | ||
183 | .owner = THIS_MODULE, | ||
184 | }; | ||
185 | |||
186 | static int __devinit max8952_pmic_probe(struct i2c_client *client, | ||
187 | const struct i2c_device_id *i2c_id) | ||
188 | { | ||
189 | struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); | ||
190 | struct max8952_platform_data *pdata = client->dev.platform_data; | ||
191 | struct max8952_data *max8952; | ||
192 | |||
193 | int ret = 0, err = 0; | ||
194 | |||
195 | if (!pdata) { | ||
196 | dev_err(&client->dev, "Require the platform data\n"); | ||
197 | return -EINVAL; | ||
198 | } | ||
199 | |||
200 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) | ||
201 | return -EIO; | ||
202 | |||
203 | max8952 = kzalloc(sizeof(struct max8952_data), GFP_KERNEL); | ||
204 | if (!max8952) | ||
205 | return -ENOMEM; | ||
206 | |||
207 | max8952->client = client; | ||
208 | max8952->dev = &client->dev; | ||
209 | max8952->pdata = pdata; | ||
210 | mutex_init(&max8952->mutex); | ||
211 | |||
212 | max8952->rdev = regulator_register(®ulator, max8952->dev, | ||
213 | &pdata->reg_data, max8952); | ||
214 | |||
215 | ret = IS_ERR(max8952->rdev); | ||
216 | if (ret) | ||
217 | dev_err(max8952->dev, "regulator init failed (%d)\n", ret); | ||
218 | |||
219 | max8952->en = !!(pdata->reg_data.constraints.boot_on); | ||
220 | max8952->vid0 = (pdata->default_mode % 2) == 1; | ||
221 | max8952->vid1 = ((pdata->default_mode >> 1) % 2) == 1; | ||
222 | |||
223 | if (gpio_is_valid(pdata->gpio_en)) { | ||
224 | if (!gpio_request(pdata->gpio_en, "MAX8952 EN")) | ||
225 | gpio_direction_output(pdata->gpio_en, max8952->en); | ||
226 | else | ||
227 | err = 1; | ||
228 | } else | ||
229 | err = 2; | ||
230 | |||
231 | if (err) { | ||
232 | dev_info(max8952->dev, "EN gpio invalid: assume that EN" | ||
233 | "is always High\n"); | ||
234 | max8952->en = 1; | ||
235 | pdata->gpio_en = -1; /* Mark invalid */ | ||
236 | } | ||
237 | |||
238 | err = 0; | ||
239 | |||
240 | if (gpio_is_valid(pdata->gpio_vid0) && | ||
241 | gpio_is_valid(pdata->gpio_vid1)) { | ||
242 | if (!gpio_request(pdata->gpio_vid0, "MAX8952 VID0")) | ||
243 | gpio_direction_output(pdata->gpio_vid0, | ||
244 | (pdata->default_mode) % 2); | ||
245 | else | ||
246 | err = 1; | ||
247 | |||
248 | if (!gpio_request(pdata->gpio_vid1, "MAX8952 VID1")) | ||
249 | gpio_direction_output(pdata->gpio_vid1, | ||
250 | (pdata->default_mode >> 1) % 2); | ||
251 | else { | ||
252 | if (!err) | ||
253 | gpio_free(pdata->gpio_vid0); | ||
254 | err = 2; | ||
255 | } | ||
256 | |||
257 | } else | ||
258 | err = 3; | ||
259 | |||
260 | if (err) { | ||
261 | dev_warn(max8952->dev, "VID0/1 gpio invalid: " | ||
262 | "DVS not avilable.\n"); | ||
263 | max8952->vid0 = 0; | ||
264 | max8952->vid1 = 0; | ||
265 | /* Mark invalid */ | ||
266 | pdata->gpio_vid0 = -1; | ||
267 | pdata->gpio_vid1 = -1; | ||
268 | |||
269 | /* Disable Pulldown of EN only */ | ||
270 | max8952_write_reg(max8952, MAX8952_REG_CONTROL, 0x60); | ||
271 | |||
272 | dev_err(max8952->dev, "DVS modes disabled because VID0 and VID1" | ||
273 | " do not have proper controls.\n"); | ||
274 | } else { | ||
275 | /* | ||
276 | * Disable Pulldown on EN, VID0, VID1 to reduce | ||
277 | * leakage current of MAX8952 assuming that MAX8952 | ||
278 | * is turned on (EN==1). Note that without having VID0/1 | ||
279 | * properly connected, turning pulldown off can be | ||
280 | * problematic. Thus, turn this off only when they are | ||
281 | * controllable by GPIO. | ||
282 | */ | ||
283 | max8952_write_reg(max8952, MAX8952_REG_CONTROL, 0x0); | ||
284 | } | ||
285 | |||
286 | max8952_write_reg(max8952, MAX8952_REG_MODE0, | ||
287 | (max8952_read_reg(max8952, | ||
288 | MAX8952_REG_MODE0) & 0xC0) | | ||
289 | (pdata->dvs_mode[0] & 0x3F)); | ||
290 | max8952_write_reg(max8952, MAX8952_REG_MODE1, | ||
291 | (max8952_read_reg(max8952, | ||
292 | MAX8952_REG_MODE1) & 0xC0) | | ||
293 | (pdata->dvs_mode[1] & 0x3F)); | ||
294 | max8952_write_reg(max8952, MAX8952_REG_MODE2, | ||
295 | (max8952_read_reg(max8952, | ||
296 | MAX8952_REG_MODE2) & 0xC0) | | ||
297 | (pdata->dvs_mode[2] & 0x3F)); | ||
298 | max8952_write_reg(max8952, MAX8952_REG_MODE3, | ||
299 | (max8952_read_reg(max8952, | ||
300 | MAX8952_REG_MODE3) & 0xC0) | | ||
301 | (pdata->dvs_mode[3] & 0x3F)); | ||
302 | |||
303 | max8952_write_reg(max8952, MAX8952_REG_SYNC, | ||
304 | (max8952_read_reg(max8952, MAX8952_REG_SYNC) & 0x3F) | | ||
305 | ((pdata->sync_freq & 0x3) << 6)); | ||
306 | max8952_write_reg(max8952, MAX8952_REG_RAMP, | ||
307 | (max8952_read_reg(max8952, MAX8952_REG_RAMP) & 0x1F) | | ||
308 | ((pdata->ramp_speed & 0x7) << 5)); | ||
309 | |||
310 | i2c_set_clientdata(client, max8952); | ||
311 | |||
312 | return ret; | ||
313 | } | ||
314 | |||
315 | static int __devexit max8952_pmic_remove(struct i2c_client *client) | ||
316 | { | ||
317 | struct max8952_data *max8952 = i2c_get_clientdata(client); | ||
318 | struct max8952_platform_data *pdata = max8952->pdata; | ||
319 | struct regulator_dev *rdev = max8952->rdev; | ||
320 | |||
321 | regulator_unregister(rdev); | ||
322 | |||
323 | gpio_free(pdata->gpio_vid0); | ||
324 | gpio_free(pdata->gpio_vid1); | ||
325 | gpio_free(pdata->gpio_en); | ||
326 | |||
327 | kfree(max8952); | ||
328 | return 0; | ||
329 | } | ||
330 | |||
331 | static const struct i2c_device_id max8952_ids[] = { | ||
332 | { "max8952", 0 }, | ||
333 | { }, | ||
334 | }; | ||
335 | MODULE_DEVICE_TABLE(i2c, max8952_ids); | ||
336 | |||
337 | static struct i2c_driver max8952_pmic_driver = { | ||
338 | .probe = max8952_pmic_probe, | ||
339 | .remove = __devexit_p(max8952_pmic_remove), | ||
340 | .driver = { | ||
341 | .name = "max8952", | ||
342 | }, | ||
343 | .id_table = max8952_ids, | ||
344 | }; | ||
345 | |||
346 | static int __init max8952_pmic_init(void) | ||
347 | { | ||
348 | return i2c_add_driver(&max8952_pmic_driver); | ||
349 | } | ||
350 | subsys_initcall(max8952_pmic_init); | ||
351 | |||
352 | static void __exit max8952_pmic_exit(void) | ||
353 | { | ||
354 | i2c_del_driver(&max8952_pmic_driver); | ||
355 | } | ||
356 | module_exit(max8952_pmic_exit); | ||
357 | |||
358 | MODULE_DESCRIPTION("MAXIM 8952 voltage regulator driver"); | ||
359 | MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); | ||
360 | MODULE_LICENSE("GPL"); | ||