blob: 43e4902d7af87485afbea85ace607d10e0045155 [file] [log] [blame]
Anuj Aggarwal30e65992009-08-21 00:39:31 +05301/*
2 * tps65023-regulator.c
3 *
4 * Supports TPS65023 Regulator
5 *
6 * Copyright (C) 2009 Texas Instrument Incorporated - http://www.ti.com/
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation version 2.
11 *
12 * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
13 * whether express or implied; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 */
17
18#include <linux/kernel.h>
19#include <linux/module.h>
20#include <linux/init.h>
21#include <linux/err.h>
22#include <linux/platform_device.h>
23#include <linux/regulator/driver.h>
24#include <linux/regulator/machine.h>
25#include <linux/i2c.h>
26#include <linux/delay.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090027#include <linux/slab.h>
Mark Brown90923352011-06-18 01:18:51 +010028#include <linux/regmap.h>
Anuj Aggarwal30e65992009-08-21 00:39:31 +053029
30/* Register definitions */
31#define TPS65023_REG_VERSION 0
32#define TPS65023_REG_PGOODZ 1
33#define TPS65023_REG_MASK 2
34#define TPS65023_REG_REG_CTRL 3
35#define TPS65023_REG_CON_CTRL 4
36#define TPS65023_REG_CON_CTRL2 5
37#define TPS65023_REG_DEF_CORE 6
38#define TPS65023_REG_DEFSLEW 7
39#define TPS65023_REG_LDO_CTRL 8
40
41/* PGOODZ bitfields */
42#define TPS65023_PGOODZ_PWRFAILZ BIT(7)
43#define TPS65023_PGOODZ_LOWBATTZ BIT(6)
44#define TPS65023_PGOODZ_VDCDC1 BIT(5)
45#define TPS65023_PGOODZ_VDCDC2 BIT(4)
46#define TPS65023_PGOODZ_VDCDC3 BIT(3)
47#define TPS65023_PGOODZ_LDO2 BIT(2)
48#define TPS65023_PGOODZ_LDO1 BIT(1)
49
50/* MASK bitfields */
51#define TPS65023_MASK_PWRFAILZ BIT(7)
52#define TPS65023_MASK_LOWBATTZ BIT(6)
53#define TPS65023_MASK_VDCDC1 BIT(5)
54#define TPS65023_MASK_VDCDC2 BIT(4)
55#define TPS65023_MASK_VDCDC3 BIT(3)
56#define TPS65023_MASK_LDO2 BIT(2)
57#define TPS65023_MASK_LDO1 BIT(1)
58
59/* REG_CTRL bitfields */
60#define TPS65023_REG_CTRL_VDCDC1_EN BIT(5)
61#define TPS65023_REG_CTRL_VDCDC2_EN BIT(4)
62#define TPS65023_REG_CTRL_VDCDC3_EN BIT(3)
63#define TPS65023_REG_CTRL_LDO2_EN BIT(2)
64#define TPS65023_REG_CTRL_LDO1_EN BIT(1)
65
Marcus Folkessonfc999b82011-08-04 13:33:49 +020066/* REG_CTRL2 bitfields */
67#define TPS65023_REG_CTRL2_GO BIT(7)
68#define TPS65023_REG_CTRL2_CORE_ADJ BIT(6)
69#define TPS65023_REG_CTRL2_DCDC2 BIT(2)
Marcus Folkessonf068ad82011-08-08 20:29:32 +020070#define TPS65023_REG_CTRL2_DCDC1 BIT(1)
71#define TPS65023_REG_CTRL2_DCDC3 BIT(0)
72
Anuj Aggarwal30e65992009-08-21 00:39:31 +053073/* LDO_CTRL bitfields */
74#define TPS65023_LDO_CTRL_LDOx_SHIFT(ldo_id) ((ldo_id)*4)
75#define TPS65023_LDO_CTRL_LDOx_MASK(ldo_id) (0xF0 >> ((ldo_id)*4))
76
77/* Number of step-down converters available */
78#define TPS65023_NUM_DCDC 3
79/* Number of LDO voltage regulators available */
80#define TPS65023_NUM_LDO 2
81/* Number of total regulators available */
82#define TPS65023_NUM_REGULATOR (TPS65023_NUM_DCDC + TPS65023_NUM_LDO)
83
84/* DCDCs */
85#define TPS65023_DCDC_1 0
86#define TPS65023_DCDC_2 1
87#define TPS65023_DCDC_3 2
88/* LDOs */
89#define TPS65023_LDO_1 3
90#define TPS65023_LDO_2 4
91
92#define TPS65023_MAX_REG_ID TPS65023_LDO_2
93
94/* Supported voltage values for regulators */
Marcus Folkesson1c3ede02011-08-08 20:29:34 +020095static const u16 VCORE_VSEL_table[] = {
Anuj Aggarwal30e65992009-08-21 00:39:31 +053096 800, 825, 850, 875,
97 900, 925, 950, 975,
98 1000, 1025, 1050, 1075,
99 1100, 1125, 1150, 1175,
100 1200, 1225, 1250, 1275,
101 1300, 1325, 1350, 1375,
102 1400, 1425, 1450, 1475,
103 1500, 1525, 1550, 1600,
104};
105
Marcus Folkesson437afd22011-08-08 20:29:35 +0200106/* Supported voltage values for LDO regulators for tps65020 */
107static const u16 TPS65020_LDO1_VSEL_table[] = {
108 1000, 1050, 1100, 1300,
109 1800, 2500, 3000, 3300,
110};
Marcus Folkesson1c3ede02011-08-08 20:29:34 +0200111
Marcus Folkesson437afd22011-08-08 20:29:35 +0200112static const u16 TPS65020_LDO2_VSEL_table[] = {
113 1000, 1050, 1100, 1300,
114 1800, 2500, 3000, 3300,
115};
Marcus Folkesson1c3ede02011-08-08 20:29:34 +0200116
117/* Supported voltage values for LDO regulators
118 * for tps65021 and tps65023 */
119static const u16 TPS65023_LDO1_VSEL_table[] = {
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530120 1000, 1100, 1300, 1800,
121 2200, 2600, 2800, 3150,
122};
123
Marcus Folkesson1c3ede02011-08-08 20:29:34 +0200124static const u16 TPS65023_LDO2_VSEL_table[] = {
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530125 1050, 1200, 1300, 1800,
126 2500, 2800, 3000, 3300,
127};
128
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530129/* Regulator specific details */
130struct tps_info {
131 const char *name;
132 unsigned min_uV;
133 unsigned max_uV;
134 bool fixed;
135 u8 table_len;
136 const u16 *table;
137};
138
139/* PMIC details */
140struct tps_pmic {
141 struct regulator_desc desc[TPS65023_NUM_REGULATOR];
142 struct i2c_client *client;
143 struct regulator_dev *rdev[TPS65023_NUM_REGULATOR];
144 const struct tps_info *info[TPS65023_NUM_REGULATOR];
Mark Brown90923352011-06-18 01:18:51 +0100145 struct regmap *regmap;
Marcus Folkesson1c3ede02011-08-08 20:29:34 +0200146 u8 core_regulator;
147};
148
149/* Struct passed as driver data */
150struct tps_driver_data {
151 const struct tps_info *info;
152 u8 core_regulator;
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530153};
154
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530155static int tps65023_dcdc_is_enabled(struct regulator_dev *dev)
156{
157 struct tps_pmic *tps = rdev_get_drvdata(dev);
158 int data, dcdc = rdev_get_id(dev);
Jonghwan Choi43530b62011-11-07 08:16:04 +0900159 int ret;
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530160 u8 shift;
161
162 if (dcdc < TPS65023_DCDC_1 || dcdc > TPS65023_DCDC_3)
163 return -EINVAL;
164
165 shift = TPS65023_NUM_REGULATOR - dcdc;
Jonghwan Choi43530b62011-11-07 08:16:04 +0900166 ret = regmap_read(tps->regmap, TPS65023_REG_REG_CTRL, &data);
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530167
Jonghwan Choi43530b62011-11-07 08:16:04 +0900168 if (ret != 0)
169 return ret;
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530170 else
171 return (data & 1<<shift) ? 1 : 0;
172}
173
174static int tps65023_ldo_is_enabled(struct regulator_dev *dev)
175{
176 struct tps_pmic *tps = rdev_get_drvdata(dev);
177 int data, ldo = rdev_get_id(dev);
Jonghwan Choi43530b62011-11-07 08:16:04 +0900178 int ret;
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530179 u8 shift;
180
181 if (ldo < TPS65023_LDO_1 || ldo > TPS65023_LDO_2)
182 return -EINVAL;
183
184 shift = (ldo == TPS65023_LDO_1 ? 1 : 2);
Jonghwan Choi43530b62011-11-07 08:16:04 +0900185 ret = regmap_read(tps->regmap, TPS65023_REG_REG_CTRL, &data);
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530186
Jonghwan Choi43530b62011-11-07 08:16:04 +0900187 if (ret != 0)
188 return ret;
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530189 else
190 return (data & 1<<shift) ? 1 : 0;
191}
192
193static int tps65023_dcdc_enable(struct regulator_dev *dev)
194{
195 struct tps_pmic *tps = rdev_get_drvdata(dev);
196 int dcdc = rdev_get_id(dev);
197 u8 shift;
198
199 if (dcdc < TPS65023_DCDC_1 || dcdc > TPS65023_DCDC_3)
200 return -EINVAL;
201
202 shift = TPS65023_NUM_REGULATOR - dcdc;
Jonghwan Choi43530b62011-11-07 08:16:04 +0900203 return regmap_update_bits(tps->regmap, TPS65023_REG_REG_CTRL, 1 << shift, 1 << shift);
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530204}
205
206static int tps65023_dcdc_disable(struct regulator_dev *dev)
207{
208 struct tps_pmic *tps = rdev_get_drvdata(dev);
209 int dcdc = rdev_get_id(dev);
210 u8 shift;
211
212 if (dcdc < TPS65023_DCDC_1 || dcdc > TPS65023_DCDC_3)
213 return -EINVAL;
214
215 shift = TPS65023_NUM_REGULATOR - dcdc;
Jonghwan Choi43530b62011-11-07 08:16:04 +0900216 return regmap_update_bits(tps->regmap, TPS65023_REG_REG_CTRL, 1 << shift, 0);
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530217}
218
219static int tps65023_ldo_enable(struct regulator_dev *dev)
220{
221 struct tps_pmic *tps = rdev_get_drvdata(dev);
222 int ldo = rdev_get_id(dev);
223 u8 shift;
224
225 if (ldo < TPS65023_LDO_1 || ldo > TPS65023_LDO_2)
226 return -EINVAL;
227
228 shift = (ldo == TPS65023_LDO_1 ? 1 : 2);
Jonghwan Choi43530b62011-11-07 08:16:04 +0900229 return regmap_update_bits(tps->regmap, TPS65023_REG_REG_CTRL, 1 << shift, 1 << shift);
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530230}
231
232static int tps65023_ldo_disable(struct regulator_dev *dev)
233{
234 struct tps_pmic *tps = rdev_get_drvdata(dev);
235 int ldo = rdev_get_id(dev);
236 u8 shift;
237
238 if (ldo < TPS65023_LDO_1 || ldo > TPS65023_LDO_2)
239 return -EINVAL;
240
241 shift = (ldo == TPS65023_LDO_1 ? 1 : 2);
Jonghwan Choi43530b62011-11-07 08:16:04 +0900242 return regmap_update_bits(tps->regmap, TPS65023_REG_REG_CTRL, 1 << shift, 0);
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530243}
244
245static int tps65023_dcdc_get_voltage(struct regulator_dev *dev)
246{
247 struct tps_pmic *tps = rdev_get_drvdata(dev);
Jonghwan Choi43530b62011-11-07 08:16:04 +0900248 int ret;
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530249 int data, dcdc = rdev_get_id(dev);
250
251 if (dcdc < TPS65023_DCDC_1 || dcdc > TPS65023_DCDC_3)
252 return -EINVAL;
253
Marcus Folkesson1c3ede02011-08-08 20:29:34 +0200254 if (dcdc == tps->core_regulator) {
Jonghwan Choi43530b62011-11-07 08:16:04 +0900255 ret = regmap_read(tps->regmap, TPS65023_REG_DEF_CORE, &data);
256 if (ret != 0)
257 return ret;
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530258 data &= (tps->info[dcdc]->table_len - 1);
259 return tps->info[dcdc]->table[data] * 1000;
260 } else
261 return tps->info[dcdc]->min_uV;
262}
263
264static int tps65023_dcdc_set_voltage(struct regulator_dev *dev,
Mark Brown3a93f2a2010-11-10 14:38:29 +0000265 int min_uV, int max_uV,
266 unsigned *selector)
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530267{
268 struct tps_pmic *tps = rdev_get_drvdata(dev);
269 int dcdc = rdev_get_id(dev);
270 int vsel;
Marcus Folkessoncc17ef32011-08-08 20:29:33 +0200271 int ret;
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530272
Marcus Folkesson1c3ede02011-08-08 20:29:34 +0200273 if (dcdc != tps->core_regulator)
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530274 return -EINVAL;
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530275 if (min_uV < tps->info[dcdc]->min_uV
276 || min_uV > tps->info[dcdc]->max_uV)
277 return -EINVAL;
278 if (max_uV < tps->info[dcdc]->min_uV
279 || max_uV > tps->info[dcdc]->max_uV)
280 return -EINVAL;
281
282 for (vsel = 0; vsel < tps->info[dcdc]->table_len; vsel++) {
283 int mV = tps->info[dcdc]->table[vsel];
284 int uV = mV * 1000;
285
286 /* Break at the first in-range value */
287 if (min_uV <= uV && uV <= max_uV)
288 break;
289 }
290
Mark Brown3a93f2a2010-11-10 14:38:29 +0000291 *selector = vsel;
292
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530293 if (vsel == tps->info[dcdc]->table_len)
Marcus Folkessoncc17ef32011-08-08 20:29:33 +0200294 goto failed;
295
Jonghwan Choi43530b62011-11-07 08:16:04 +0900296 ret = regmap_write(tps->regmap, TPS65023_REG_DEF_CORE, vsel);
Marcus Folkessoncc17ef32011-08-08 20:29:33 +0200297
298 /* Tell the chip that we have changed the value in DEFCORE
299 * and its time to update the core voltage
300 */
Jonghwan Choi43530b62011-11-07 08:16:04 +0900301 regmap_update_bits(tps->regmap, TPS65023_REG_CON_CTRL2,
302 TPS65023_REG_CTRL2_GO, TPS65023_REG_CTRL2_GO);
Marcus Folkessoncc17ef32011-08-08 20:29:33 +0200303
304 return ret;
305
306failed:
307 return -EINVAL;
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530308}
309
310static int tps65023_ldo_get_voltage(struct regulator_dev *dev)
311{
312 struct tps_pmic *tps = rdev_get_drvdata(dev);
313 int data, ldo = rdev_get_id(dev);
Jonghwan Choi43530b62011-11-07 08:16:04 +0900314 int ret;
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530315
316 if (ldo < TPS65023_LDO_1 || ldo > TPS65023_LDO_2)
317 return -EINVAL;
318
Jonghwan Choi43530b62011-11-07 08:16:04 +0900319 ret = regmap_read(tps->regmap, TPS65023_REG_LDO_CTRL, &data);
320 if (ret != 0)
321 return ret;
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530322
323 data >>= (TPS65023_LDO_CTRL_LDOx_SHIFT(ldo - TPS65023_LDO_1));
324 data &= (tps->info[ldo]->table_len - 1);
325 return tps->info[ldo]->table[data] * 1000;
326}
327
328static int tps65023_ldo_set_voltage(struct regulator_dev *dev,
Mark Brown3a93f2a2010-11-10 14:38:29 +0000329 int min_uV, int max_uV, unsigned *selector)
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530330{
331 struct tps_pmic *tps = rdev_get_drvdata(dev);
332 int data, vsel, ldo = rdev_get_id(dev);
Jonghwan Choi43530b62011-11-07 08:16:04 +0900333 int ret;
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530334
335 if (ldo < TPS65023_LDO_1 || ldo > TPS65023_LDO_2)
336 return -EINVAL;
337
338 if (min_uV < tps->info[ldo]->min_uV || min_uV > tps->info[ldo]->max_uV)
339 return -EINVAL;
340 if (max_uV < tps->info[ldo]->min_uV || max_uV > tps->info[ldo]->max_uV)
341 return -EINVAL;
342
343 for (vsel = 0; vsel < tps->info[ldo]->table_len; vsel++) {
344 int mV = tps->info[ldo]->table[vsel];
345 int uV = mV * 1000;
346
347 /* Break at the first in-range value */
348 if (min_uV <= uV && uV <= max_uV)
349 break;
350 }
351
352 if (vsel == tps->info[ldo]->table_len)
353 return -EINVAL;
354
Mark Brown3a93f2a2010-11-10 14:38:29 +0000355 *selector = vsel;
356
Jonghwan Choi43530b62011-11-07 08:16:04 +0900357 ret = regmap_read(tps->regmap, TPS65023_REG_LDO_CTRL, &data);
358 if (ret != 0)
359 return ret;
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530360
361 data &= TPS65023_LDO_CTRL_LDOx_MASK(ldo - TPS65023_LDO_1);
362 data |= (vsel << (TPS65023_LDO_CTRL_LDOx_SHIFT(ldo - TPS65023_LDO_1)));
Jonghwan Choi43530b62011-11-07 08:16:04 +0900363 return regmap_write(tps->regmap, TPS65023_REG_LDO_CTRL, data);
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530364}
365
366static int tps65023_dcdc_list_voltage(struct regulator_dev *dev,
367 unsigned selector)
368{
369 struct tps_pmic *tps = rdev_get_drvdata(dev);
370 int dcdc = rdev_get_id(dev);
371
372 if (dcdc < TPS65023_DCDC_1 || dcdc > TPS65023_DCDC_3)
373 return -EINVAL;
374
Marcus Folkesson1c3ede02011-08-08 20:29:34 +0200375 if (dcdc == tps->core_regulator) {
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530376 if (selector >= tps->info[dcdc]->table_len)
377 return -EINVAL;
378 else
379 return tps->info[dcdc]->table[selector] * 1000;
380 } else
381 return tps->info[dcdc]->min_uV;
382}
383
384static int tps65023_ldo_list_voltage(struct regulator_dev *dev,
385 unsigned selector)
386{
387 struct tps_pmic *tps = rdev_get_drvdata(dev);
388 int ldo = rdev_get_id(dev);
389
390 if (ldo < TPS65023_LDO_1 || ldo > TPS65023_LDO_2)
391 return -EINVAL;
392
393 if (selector >= tps->info[ldo]->table_len)
394 return -EINVAL;
395 else
396 return tps->info[ldo]->table[selector] * 1000;
397}
398
399/* Operations permitted on VDCDCx */
400static struct regulator_ops tps65023_dcdc_ops = {
401 .is_enabled = tps65023_dcdc_is_enabled,
402 .enable = tps65023_dcdc_enable,
403 .disable = tps65023_dcdc_disable,
404 .get_voltage = tps65023_dcdc_get_voltage,
405 .set_voltage = tps65023_dcdc_set_voltage,
406 .list_voltage = tps65023_dcdc_list_voltage,
407};
408
409/* Operations permitted on LDOx */
410static struct regulator_ops tps65023_ldo_ops = {
411 .is_enabled = tps65023_ldo_is_enabled,
412 .enable = tps65023_ldo_enable,
413 .disable = tps65023_ldo_disable,
414 .get_voltage = tps65023_ldo_get_voltage,
415 .set_voltage = tps65023_ldo_set_voltage,
416 .list_voltage = tps65023_ldo_list_voltage,
417};
418
Mark Brown90923352011-06-18 01:18:51 +0100419static struct regmap_config tps65023_regmap_config = {
420 .reg_bits = 8,
421 .val_bits = 8,
422};
423
Dmitry Torokhov54d13ab2010-02-23 23:38:06 -0800424static int __devinit tps_65023_probe(struct i2c_client *client,
425 const struct i2c_device_id *id)
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530426{
Marcus Folkesson1c3ede02011-08-08 20:29:34 +0200427 const struct tps_driver_data *drv_data = (void *)id->driver_data;
428 const struct tps_info *info = drv_data->info;
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530429 struct regulator_init_data *init_data;
430 struct regulator_dev *rdev;
431 struct tps_pmic *tps;
432 int i;
Dmitry Torokhov54d13ab2010-02-23 23:38:06 -0800433 int error;
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530434
435 if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
436 return -EIO;
437
438 /**
439 * init_data points to array of regulator_init structures
440 * coming from the board-evm file.
441 */
442 init_data = client->dev.platform_data;
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530443 if (!init_data)
444 return -EIO;
445
446 tps = kzalloc(sizeof(*tps), GFP_KERNEL);
447 if (!tps)
448 return -ENOMEM;
449
Mark Brown90923352011-06-18 01:18:51 +0100450 tps->regmap = regmap_init_i2c(client, &tps65023_regmap_config);
451 if (IS_ERR(tps->regmap)) {
452 error = PTR_ERR(tps->regmap);
453 dev_err(&client->dev, "Failed to allocate register map: %d\n",
454 error);
455 goto fail_alloc;
456 }
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530457
458 /* common for all regulators */
459 tps->client = client;
Marcus Folkesson1c3ede02011-08-08 20:29:34 +0200460 tps->core_regulator = drv_data->core_regulator;
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530461
462 for (i = 0; i < TPS65023_NUM_REGULATOR; i++, info++, init_data++) {
463 /* Store regulator specific information */
464 tps->info[i] = info;
465
466 tps->desc[i].name = info->name;
Axel Lin77fa44d2011-05-12 13:47:50 +0800467 tps->desc[i].id = i;
Marcus Folkesson1c3ede02011-08-08 20:29:34 +0200468 tps->desc[i].n_voltages = info->table_len;
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530469 tps->desc[i].ops = (i > TPS65023_DCDC_3 ?
470 &tps65023_ldo_ops : &tps65023_dcdc_ops);
471 tps->desc[i].type = REGULATOR_VOLTAGE;
472 tps->desc[i].owner = THIS_MODULE;
473
474 /* Register the regulators */
475 rdev = regulator_register(&tps->desc[i], &client->dev,
Rajendra Nayak2c043bc2011-11-18 16:47:19 +0530476 init_data, tps, NULL);
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530477 if (IS_ERR(rdev)) {
478 dev_err(&client->dev, "failed to register %s\n",
479 id->name);
Dmitry Torokhov54d13ab2010-02-23 23:38:06 -0800480 error = PTR_ERR(rdev);
481 goto fail;
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530482 }
483
484 /* Save regulator for cleanup */
485 tps->rdev[i] = rdev;
486 }
487
488 i2c_set_clientdata(client, tps);
489
Marcus Folkessonfc999b82011-08-04 13:33:49 +0200490 /* Enable setting output voltage by I2C */
Jonghwan Choi43530b62011-11-07 08:16:04 +0900491 regmap_update_bits(tps->regmap, TPS65023_REG_CON_CTRL2,
492 TPS65023_REG_CTRL2_CORE_ADJ, TPS65023_REG_CTRL2_CORE_ADJ);
Marcus Folkessonfc999b82011-08-04 13:33:49 +0200493
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530494 return 0;
Dmitry Torokhov54d13ab2010-02-23 23:38:06 -0800495
496 fail:
497 while (--i >= 0)
498 regulator_unregister(tps->rdev[i]);
499
Mark Brown90923352011-06-18 01:18:51 +0100500 regmap_exit(tps->regmap);
501 fail_alloc:
Dmitry Torokhov54d13ab2010-02-23 23:38:06 -0800502 kfree(tps);
503 return error;
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530504}
505
506/**
507 * tps_65023_remove - TPS65023 driver i2c remove handler
508 * @client: i2c driver client device structure
509 *
510 * Unregister TPS driver as an i2c client device driver
511 */
512static int __devexit tps_65023_remove(struct i2c_client *client)
513{
514 struct tps_pmic *tps = i2c_get_clientdata(client);
515 int i;
516
517 for (i = 0; i < TPS65023_NUM_REGULATOR; i++)
518 regulator_unregister(tps->rdev[i]);
519
Mark Brown90923352011-06-18 01:18:51 +0100520 regmap_exit(tps->regmap);
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530521 kfree(tps);
522
523 return 0;
524}
525
Marcus Folkesson437afd22011-08-08 20:29:35 +0200526static const struct tps_info tps65020_regs[] = {
527 {
528 .name = "VDCDC1",
529 .min_uV = 3300000,
530 .max_uV = 3300000,
531 .fixed = 1,
532 },
533 {
534 .name = "VDCDC2",
535 .min_uV = 1800000,
536 .max_uV = 1800000,
537 .fixed = 1,
538 },
539 {
540 .name = "VDCDC3",
541 .min_uV = 800000,
542 .max_uV = 1600000,
543 .table_len = ARRAY_SIZE(VCORE_VSEL_table),
544 .table = VCORE_VSEL_table,
545 },
546
547 {
548 .name = "LDO1",
549 .min_uV = 1000000,
550 .max_uV = 3150000,
551 .table_len = ARRAY_SIZE(TPS65020_LDO1_VSEL_table),
552 .table = TPS65020_LDO1_VSEL_table,
553 },
554 {
555 .name = "LDO2",
556 .min_uV = 1050000,
557 .max_uV = 3300000,
558 .table_len = ARRAY_SIZE(TPS65020_LDO2_VSEL_table),
559 .table = TPS65020_LDO2_VSEL_table,
560 },
561};
562
Marcus Folkesson1c3ede02011-08-08 20:29:34 +0200563static const struct tps_info tps65021_regs[] = {
564 {
565 .name = "VDCDC1",
566 .min_uV = 3300000,
567 .max_uV = 3300000,
568 .fixed = 1,
569 },
570 {
571 .name = "VDCDC2",
572 .min_uV = 1800000,
573 .max_uV = 1800000,
574 .fixed = 1,
575 },
576 {
577 .name = "VDCDC3",
578 .min_uV = 800000,
579 .max_uV = 1600000,
580 .table_len = ARRAY_SIZE(VCORE_VSEL_table),
581 .table = VCORE_VSEL_table,
582 },
583 {
584 .name = "LDO1",
585 .min_uV = 1000000,
586 .max_uV = 3150000,
587 .table_len = ARRAY_SIZE(TPS65023_LDO1_VSEL_table),
588 .table = TPS65023_LDO1_VSEL_table,
589 },
590 {
591 .name = "LDO2",
592 .min_uV = 1050000,
593 .max_uV = 3300000,
594 .table_len = ARRAY_SIZE(TPS65023_LDO2_VSEL_table),
595 .table = TPS65023_LDO2_VSEL_table,
596 },
597};
598
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530599static const struct tps_info tps65023_regs[] = {
600 {
601 .name = "VDCDC1",
602 .min_uV = 800000,
603 .max_uV = 1600000,
Marcus Folkesson1c3ede02011-08-08 20:29:34 +0200604 .table_len = ARRAY_SIZE(VCORE_VSEL_table),
605 .table = VCORE_VSEL_table,
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530606 },
607 {
608 .name = "VDCDC2",
609 .min_uV = 3300000,
610 .max_uV = 3300000,
611 .fixed = 1,
612 },
613 {
614 .name = "VDCDC3",
615 .min_uV = 1800000,
616 .max_uV = 1800000,
617 .fixed = 1,
618 },
619 {
620 .name = "LDO1",
621 .min_uV = 1000000,
622 .max_uV = 3150000,
Marcus Folkesson1c3ede02011-08-08 20:29:34 +0200623 .table_len = ARRAY_SIZE(TPS65023_LDO1_VSEL_table),
624 .table = TPS65023_LDO1_VSEL_table,
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530625 },
626 {
627 .name = "LDO2",
628 .min_uV = 1050000,
629 .max_uV = 3300000,
Marcus Folkesson1c3ede02011-08-08 20:29:34 +0200630 .table_len = ARRAY_SIZE(TPS65023_LDO2_VSEL_table),
631 .table = TPS65023_LDO2_VSEL_table,
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530632 },
633};
634
Marcus Folkesson437afd22011-08-08 20:29:35 +0200635static struct tps_driver_data tps65020_drv_data = {
636 .info = tps65020_regs,
637 .core_regulator = TPS65023_DCDC_3,
638};
639
Marcus Folkesson1c3ede02011-08-08 20:29:34 +0200640static struct tps_driver_data tps65021_drv_data = {
641 .info = tps65021_regs,
642 .core_regulator = TPS65023_DCDC_3,
643};
644
645static struct tps_driver_data tps65023_drv_data = {
646 .info = tps65023_regs,
647 .core_regulator = TPS65023_DCDC_1,
648};
649
Liam Girdwood9e108d32009-08-24 10:31:34 +0100650static const struct i2c_device_id tps_65023_id[] = {
651 {.name = "tps65023",
Marcus Folkesson1c3ede02011-08-08 20:29:34 +0200652 .driver_data = (unsigned long) &tps65023_drv_data},
Marek Vasut1880a2f2010-06-24 15:49:49 +0200653 {.name = "tps65021",
Marcus Folkesson1c3ede02011-08-08 20:29:34 +0200654 .driver_data = (unsigned long) &tps65021_drv_data,},
Marcus Folkesson437afd22011-08-08 20:29:35 +0200655 {.name = "tps65020",
656 .driver_data = (unsigned long) &tps65020_drv_data},
Liam Girdwood9e108d32009-08-24 10:31:34 +0100657 { },
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530658};
659
660MODULE_DEVICE_TABLE(i2c, tps_65023_id);
661
662static struct i2c_driver tps_65023_i2c_driver = {
663 .driver = {
664 .name = "tps65023",
665 .owner = THIS_MODULE,
666 },
667 .probe = tps_65023_probe,
668 .remove = __devexit_p(tps_65023_remove),
Liam Girdwood9e108d32009-08-24 10:31:34 +0100669 .id_table = tps_65023_id,
Anuj Aggarwal30e65992009-08-21 00:39:31 +0530670};
671
672/**
673 * tps_65023_init
674 *
675 * Module init function
676 */
677static int __init tps_65023_init(void)
678{
679 return i2c_add_driver(&tps_65023_i2c_driver);
680}
681subsys_initcall(tps_65023_init);
682
683/**
684 * tps_65023_cleanup
685 *
686 * Module exit function
687 */
688static void __exit tps_65023_cleanup(void)
689{
690 i2c_del_driver(&tps_65023_i2c_driver);
691}
692module_exit(tps_65023_cleanup);
693
694MODULE_AUTHOR("Texas Instruments");
695MODULE_DESCRIPTION("TPS65023 voltage regulator driver");
Liam Girdwood9e108d32009-08-24 10:31:34 +0100696MODULE_LICENSE("GPL v2");