Blame view

drivers/regulator/tps6507x-regulator.c 15.6 KB
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  /*
   * tps6507x-regulator.c
   *
   * Regulator driver for TPS65073 PMIC
   *
   * Copyright (C) 2009 Texas Instrument Incorporated - http://www.ti.com/
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License as
   * published by the Free Software Foundation version 2.
   *
   * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
   * whether express or implied; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * General Public License for more details.
   */
  
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/err.h>
  #include <linux/platform_device.h>
  #include <linux/regulator/driver.h>
  #include <linux/regulator/machine.h>
7d14831e2   Anuj Aggarwal   regulator: tps650...
25
  #include <linux/regulator/tps6507x.h>
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
26
  #include <linux/delay.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
27
  #include <linux/slab.h>
d183fcc97   Todd Fischer   mfd: Move TPS6507...
28
  #include <linux/mfd/tps6507x.h>
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
  
  /* DCDC's */
  #define TPS6507X_DCDC_1				0
  #define TPS6507X_DCDC_2				1
  #define TPS6507X_DCDC_3				2
  /* LDOs */
  #define TPS6507X_LDO_1				3
  #define TPS6507X_LDO_2				4
  
  #define TPS6507X_MAX_REG_ID			TPS6507X_LDO_2
  
  /* Number of step-down converters available */
  #define TPS6507X_NUM_DCDC			3
  /* Number of LDO voltage regulators  available */
  #define TPS6507X_NUM_LDO			2
  /* Number of total regulators available */
  #define TPS6507X_NUM_REGULATOR		(TPS6507X_NUM_DCDC + TPS6507X_NUM_LDO)
  
  /* Supported voltage values for regulators (in milliVolts) */
  static const u16 VDCDCx_VSEL_table[] = {
  	725, 750, 775, 800,
  	825, 850, 875, 900,
  	925, 950, 975, 1000,
  	1025, 1050, 1075, 1100,
  	1125, 1150, 1175, 1200,
  	1225, 1250, 1275, 1300,
  	1325, 1350, 1375, 1400,
  	1425, 1450, 1475, 1500,
  	1550, 1600, 1650, 1700,
  	1750, 1800, 1850, 1900,
  	1950, 2000, 2050, 2100,
  	2150, 2200, 2250, 2300,
  	2350, 2400, 2450, 2500,
  	2550, 2600, 2650, 2700,
  	2750, 2800, 2850, 2900,
  	3000, 3100, 3200, 3300,
  };
  
  static const u16 LDO1_VSEL_table[] = {
  	1000, 1100, 1200, 1250,
  	1300, 1350, 1400, 1500,
  	1600, 1800, 2500, 2750,
  	2800, 3000, 3100, 3300,
  };
  
  static const u16 LDO2_VSEL_table[] = {
  	725, 750, 775, 800,
  	825, 850, 875, 900,
  	925, 950, 975, 1000,
  	1025, 1050, 1075, 1100,
  	1125, 1150, 1175, 1200,
  	1225, 1250, 1275, 1300,
  	1325, 1350, 1375, 1400,
  	1425, 1450, 1475, 1500,
  	1550, 1600, 1650, 1700,
  	1750, 1800, 1850, 1900,
  	1950, 2000, 2050, 2100,
  	2150, 2200, 2250, 2300,
  	2350, 2400, 2450, 2500,
  	2550, 2600, 2650, 2700,
  	2750, 2800, 2850, 2900,
  	3000, 3100, 3200, 3300,
  };
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
92
93
94
95
96
97
  struct tps_info {
  	const char *name;
  	unsigned min_uV;
  	unsigned max_uV;
  	u8 table_len;
  	const u16 *table;
7d14831e2   Anuj Aggarwal   regulator: tps650...
98
99
100
  
  	/* Does DCDC high or the low register defines output voltage? */
  	bool defdcdc_default;
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
101
  };
7d14831e2   Anuj Aggarwal   regulator: tps650...
102
  static struct tps_info tps6507x_pmic_regs[] = {
31dd6a267   Todd Fischer   mfd: Add TPS6507x...
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
  	{
  		.name = "VDCDC1",
  		.min_uV = 725000,
  		.max_uV = 3300000,
  		.table_len = ARRAY_SIZE(VDCDCx_VSEL_table),
  		.table = VDCDCx_VSEL_table,
  	},
  	{
  		.name = "VDCDC2",
  		.min_uV = 725000,
  		.max_uV = 3300000,
  		.table_len = ARRAY_SIZE(VDCDCx_VSEL_table),
  		.table = VDCDCx_VSEL_table,
  	},
  	{
  		.name = "VDCDC3",
  		.min_uV = 725000,
  		.max_uV = 3300000,
  		.table_len = ARRAY_SIZE(VDCDCx_VSEL_table),
  		.table = VDCDCx_VSEL_table,
  	},
  	{
  		.name = "LDO1",
  		.min_uV = 1000000,
  		.max_uV = 3300000,
  		.table_len = ARRAY_SIZE(LDO1_VSEL_table),
  		.table = LDO1_VSEL_table,
  	},
  	{
  		.name = "LDO2",
  		.min_uV = 725000,
  		.max_uV = 3300000,
  		.table_len = ARRAY_SIZE(LDO2_VSEL_table),
  		.table = LDO2_VSEL_table,
  	},
  };
4ce5ba5ba   Todd Fischer   regulator: tps605...
139
  struct tps6507x_pmic {
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
140
  	struct regulator_desc desc[TPS6507X_NUM_REGULATOR];
31dd6a267   Todd Fischer   mfd: Add TPS6507x...
141
  	struct tps6507x_dev *mfd;
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
142
  	struct regulator_dev *rdev[TPS6507X_NUM_REGULATOR];
7d14831e2   Anuj Aggarwal   regulator: tps650...
143
  	struct tps_info *info[TPS6507X_NUM_REGULATOR];
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
144
145
  	struct mutex io_lock;
  };
4ce5ba5ba   Todd Fischer   regulator: tps605...
146
  static inline int tps6507x_pmic_read(struct tps6507x_pmic *tps, u8 reg)
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
147
  {
31dd6a267   Todd Fischer   mfd: Add TPS6507x...
148
149
150
151
152
153
154
155
156
  	u8 val;
  	int err;
  
  	err = tps->mfd->read_dev(tps->mfd, reg, 1, &val);
  
  	if (err)
  		return err;
  
  	return val;
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
157
  }
4ce5ba5ba   Todd Fischer   regulator: tps605...
158
  static inline int tps6507x_pmic_write(struct tps6507x_pmic *tps, u8 reg, u8 val)
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
159
  {
31dd6a267   Todd Fischer   mfd: Add TPS6507x...
160
  	return tps->mfd->write_dev(tps->mfd, reg, 1, &val);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
161
  }
4ce5ba5ba   Todd Fischer   regulator: tps605...
162
  static int tps6507x_pmic_set_bits(struct tps6507x_pmic *tps, u8 reg, u8 mask)
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
163
164
165
166
  {
  	int err, data;
  
  	mutex_lock(&tps->io_lock);
4ce5ba5ba   Todd Fischer   regulator: tps605...
167
  	data = tps6507x_pmic_read(tps, reg);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
168
  	if (data < 0) {
31dd6a267   Todd Fischer   mfd: Add TPS6507x...
169
170
  		dev_err(tps->mfd->dev, "Read from reg 0x%x failed
  ", reg);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
171
172
173
174
175
  		err = data;
  		goto out;
  	}
  
  	data |= mask;
4ce5ba5ba   Todd Fischer   regulator: tps605...
176
  	err = tps6507x_pmic_write(tps, reg, data);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
177
  	if (err)
31dd6a267   Todd Fischer   mfd: Add TPS6507x...
178
179
  		dev_err(tps->mfd->dev, "Write for reg 0x%x failed
  ", reg);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
180
181
182
183
184
  
  out:
  	mutex_unlock(&tps->io_lock);
  	return err;
  }
4ce5ba5ba   Todd Fischer   regulator: tps605...
185
  static int tps6507x_pmic_clear_bits(struct tps6507x_pmic *tps, u8 reg, u8 mask)
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
186
187
188
189
  {
  	int err, data;
  
  	mutex_lock(&tps->io_lock);
4ce5ba5ba   Todd Fischer   regulator: tps605...
190
  	data = tps6507x_pmic_read(tps, reg);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
191
  	if (data < 0) {
31dd6a267   Todd Fischer   mfd: Add TPS6507x...
192
193
  		dev_err(tps->mfd->dev, "Read from reg 0x%x failed
  ", reg);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
194
195
196
197
198
  		err = data;
  		goto out;
  	}
  
  	data &= ~mask;
4ce5ba5ba   Todd Fischer   regulator: tps605...
199
  	err = tps6507x_pmic_write(tps, reg, data);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
200
  	if (err)
31dd6a267   Todd Fischer   mfd: Add TPS6507x...
201
202
  		dev_err(tps->mfd->dev, "Write for reg 0x%x failed
  ", reg);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
203
204
205
206
207
  
  out:
  	mutex_unlock(&tps->io_lock);
  	return err;
  }
4ce5ba5ba   Todd Fischer   regulator: tps605...
208
  static int tps6507x_pmic_reg_read(struct tps6507x_pmic *tps, u8 reg)
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
209
210
211
212
  {
  	int data;
  
  	mutex_lock(&tps->io_lock);
4ce5ba5ba   Todd Fischer   regulator: tps605...
213
  	data = tps6507x_pmic_read(tps, reg);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
214
  	if (data < 0)
31dd6a267   Todd Fischer   mfd: Add TPS6507x...
215
216
  		dev_err(tps->mfd->dev, "Read from reg 0x%x failed
  ", reg);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
217
218
219
220
  
  	mutex_unlock(&tps->io_lock);
  	return data;
  }
4ce5ba5ba   Todd Fischer   regulator: tps605...
221
  static int tps6507x_pmic_reg_write(struct tps6507x_pmic *tps, u8 reg, u8 val)
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
222
223
224
225
  {
  	int err;
  
  	mutex_lock(&tps->io_lock);
4ce5ba5ba   Todd Fischer   regulator: tps605...
226
  	err = tps6507x_pmic_write(tps, reg, val);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
227
  	if (err < 0)
31dd6a267   Todd Fischer   mfd: Add TPS6507x...
228
229
  		dev_err(tps->mfd->dev, "Write for reg 0x%x failed
  ", reg);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
230
231
232
233
  
  	mutex_unlock(&tps->io_lock);
  	return err;
  }
4ce5ba5ba   Todd Fischer   regulator: tps605...
234
  static int tps6507x_pmic_dcdc_is_enabled(struct regulator_dev *dev)
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
235
  {
4ce5ba5ba   Todd Fischer   regulator: tps605...
236
  	struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
237
238
239
240
241
242
243
  	int data, dcdc = rdev_get_id(dev);
  	u8 shift;
  
  	if (dcdc < TPS6507X_DCDC_1 || dcdc > TPS6507X_DCDC_3)
  		return -EINVAL;
  
  	shift = TPS6507X_MAX_REG_ID - dcdc;
4ce5ba5ba   Todd Fischer   regulator: tps605...
244
  	data = tps6507x_pmic_reg_read(tps, TPS6507X_REG_CON_CTRL1);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
245
246
247
248
249
250
  
  	if (data < 0)
  		return data;
  	else
  		return (data & 1<<shift) ? 1 : 0;
  }
4ce5ba5ba   Todd Fischer   regulator: tps605...
251
  static int tps6507x_pmic_ldo_is_enabled(struct regulator_dev *dev)
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
252
  {
4ce5ba5ba   Todd Fischer   regulator: tps605...
253
  	struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
254
255
256
257
258
259
260
  	int data, ldo = rdev_get_id(dev);
  	u8 shift;
  
  	if (ldo < TPS6507X_LDO_1 || ldo > TPS6507X_LDO_2)
  		return -EINVAL;
  
  	shift = TPS6507X_MAX_REG_ID - ldo;
4ce5ba5ba   Todd Fischer   regulator: tps605...
261
  	data = tps6507x_pmic_reg_read(tps, TPS6507X_REG_CON_CTRL1);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
262
263
264
265
266
267
  
  	if (data < 0)
  		return data;
  	else
  		return (data & 1<<shift) ? 1 : 0;
  }
4ce5ba5ba   Todd Fischer   regulator: tps605...
268
  static int tps6507x_pmic_dcdc_enable(struct regulator_dev *dev)
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
269
  {
4ce5ba5ba   Todd Fischer   regulator: tps605...
270
  	struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
271
272
273
274
275
276
277
  	int dcdc = rdev_get_id(dev);
  	u8 shift;
  
  	if (dcdc < TPS6507X_DCDC_1 || dcdc > TPS6507X_DCDC_3)
  		return -EINVAL;
  
  	shift = TPS6507X_MAX_REG_ID - dcdc;
4ce5ba5ba   Todd Fischer   regulator: tps605...
278
  	return tps6507x_pmic_set_bits(tps, TPS6507X_REG_CON_CTRL1, 1 << shift);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
279
  }
4ce5ba5ba   Todd Fischer   regulator: tps605...
280
  static int tps6507x_pmic_dcdc_disable(struct regulator_dev *dev)
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
281
  {
4ce5ba5ba   Todd Fischer   regulator: tps605...
282
  	struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
283
284
285
286
287
288
289
  	int dcdc = rdev_get_id(dev);
  	u8 shift;
  
  	if (dcdc < TPS6507X_DCDC_1 || dcdc > TPS6507X_DCDC_3)
  		return -EINVAL;
  
  	shift = TPS6507X_MAX_REG_ID - dcdc;
4ce5ba5ba   Todd Fischer   regulator: tps605...
290
291
  	return tps6507x_pmic_clear_bits(tps, TPS6507X_REG_CON_CTRL1,
  					1 << shift);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
292
  }
4ce5ba5ba   Todd Fischer   regulator: tps605...
293
  static int tps6507x_pmic_ldo_enable(struct regulator_dev *dev)
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
294
  {
4ce5ba5ba   Todd Fischer   regulator: tps605...
295
  	struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
296
297
298
299
300
301
302
  	int ldo = rdev_get_id(dev);
  	u8 shift;
  
  	if (ldo < TPS6507X_LDO_1 || ldo > TPS6507X_LDO_2)
  		return -EINVAL;
  
  	shift = TPS6507X_MAX_REG_ID - ldo;
4ce5ba5ba   Todd Fischer   regulator: tps605...
303
  	return tps6507x_pmic_set_bits(tps, TPS6507X_REG_CON_CTRL1, 1 << shift);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
304
  }
4ce5ba5ba   Todd Fischer   regulator: tps605...
305
  static int tps6507x_pmic_ldo_disable(struct regulator_dev *dev)
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
306
  {
4ce5ba5ba   Todd Fischer   regulator: tps605...
307
  	struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
308
309
310
311
312
313
314
  	int ldo = rdev_get_id(dev);
  	u8 shift;
  
  	if (ldo < TPS6507X_LDO_1 || ldo > TPS6507X_LDO_2)
  		return -EINVAL;
  
  	shift = TPS6507X_MAX_REG_ID - ldo;
4ce5ba5ba   Todd Fischer   regulator: tps605...
315
316
  	return tps6507x_pmic_clear_bits(tps, TPS6507X_REG_CON_CTRL1,
  					1 << shift);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
317
  }
4ce5ba5ba   Todd Fischer   regulator: tps605...
318
  static int tps6507x_pmic_dcdc_get_voltage(struct regulator_dev *dev)
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
319
  {
4ce5ba5ba   Todd Fischer   regulator: tps605...
320
  	struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
321
322
323
324
325
326
327
328
  	int data, dcdc = rdev_get_id(dev);
  	u8 reg;
  
  	switch (dcdc) {
  	case TPS6507X_DCDC_1:
  		reg = TPS6507X_REG_DEFDCDC1;
  		break;
  	case TPS6507X_DCDC_2:
7d14831e2   Anuj Aggarwal   regulator: tps650...
329
330
331
332
  		if (tps->info[dcdc]->defdcdc_default)
  			reg = TPS6507X_REG_DEFDCDC2_HIGH;
  		else
  			reg = TPS6507X_REG_DEFDCDC2_LOW;
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
333
334
  		break;
  	case TPS6507X_DCDC_3:
7d14831e2   Anuj Aggarwal   regulator: tps650...
335
336
337
338
  		if (tps->info[dcdc]->defdcdc_default)
  			reg = TPS6507X_REG_DEFDCDC3_HIGH;
  		else
  			reg = TPS6507X_REG_DEFDCDC3_LOW;
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
339
340
341
342
  		break;
  	default:
  		return -EINVAL;
  	}
4ce5ba5ba   Todd Fischer   regulator: tps605...
343
  	data = tps6507x_pmic_reg_read(tps, reg);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
344
345
346
347
348
349
  	if (data < 0)
  		return data;
  
  	data &= TPS6507X_DEFDCDCX_DCDC_MASK;
  	return tps->info[dcdc]->table[data] * 1000;
  }
4ce5ba5ba   Todd Fischer   regulator: tps605...
350
  static int tps6507x_pmic_dcdc_set_voltage(struct regulator_dev *dev,
3a93f2a9f   Mark Brown   regulator: Report...
351
352
  					  int min_uV, int max_uV,
  					  unsigned *selector)
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
353
  {
4ce5ba5ba   Todd Fischer   regulator: tps605...
354
  	struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
355
356
357
358
359
360
361
362
  	int data, vsel, dcdc = rdev_get_id(dev);
  	u8 reg;
  
  	switch (dcdc) {
  	case TPS6507X_DCDC_1:
  		reg = TPS6507X_REG_DEFDCDC1;
  		break;
  	case TPS6507X_DCDC_2:
7d14831e2   Anuj Aggarwal   regulator: tps650...
363
364
365
366
  		if (tps->info[dcdc]->defdcdc_default)
  			reg = TPS6507X_REG_DEFDCDC2_HIGH;
  		else
  			reg = TPS6507X_REG_DEFDCDC2_LOW;
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
367
368
  		break;
  	case TPS6507X_DCDC_3:
7d14831e2   Anuj Aggarwal   regulator: tps650...
369
370
371
372
  		if (tps->info[dcdc]->defdcdc_default)
  			reg = TPS6507X_REG_DEFDCDC3_HIGH;
  		else
  			reg = TPS6507X_REG_DEFDCDC3_LOW;
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
  		break;
  	default:
  		return -EINVAL;
  	}
  
  	if (min_uV < tps->info[dcdc]->min_uV
  		|| min_uV > tps->info[dcdc]->max_uV)
  		return -EINVAL;
  	if (max_uV < tps->info[dcdc]->min_uV
  		|| max_uV > tps->info[dcdc]->max_uV)
  		return -EINVAL;
  
  	for (vsel = 0; vsel < tps->info[dcdc]->table_len; vsel++) {
  		int mV = tps->info[dcdc]->table[vsel];
  		int uV = mV * 1000;
  
  		/* Break at the first in-range value */
  		if (min_uV <= uV && uV <= max_uV)
  			break;
  	}
  
  	/* write to the register in case we found a match */
  	if (vsel == tps->info[dcdc]->table_len)
  		return -EINVAL;
3a93f2a9f   Mark Brown   regulator: Report...
397
  	*selector = vsel;
4ce5ba5ba   Todd Fischer   regulator: tps605...
398
  	data = tps6507x_pmic_reg_read(tps, reg);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
399
400
401
402
403
  	if (data < 0)
  		return data;
  
  	data &= ~TPS6507X_DEFDCDCX_DCDC_MASK;
  	data |= vsel;
4ce5ba5ba   Todd Fischer   regulator: tps605...
404
  	return tps6507x_pmic_reg_write(tps, reg, data);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
405
  }
4ce5ba5ba   Todd Fischer   regulator: tps605...
406
  static int tps6507x_pmic_ldo_get_voltage(struct regulator_dev *dev)
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
407
  {
4ce5ba5ba   Todd Fischer   regulator: tps605...
408
  	struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
409
410
411
412
413
414
415
416
417
418
419
420
  	int data, ldo = rdev_get_id(dev);
  	u8 reg, mask;
  
  	if (ldo < TPS6507X_LDO_1 || ldo > TPS6507X_LDO_2)
  		return -EINVAL;
  	else {
  		reg = (ldo == TPS6507X_LDO_1 ?
  			TPS6507X_REG_LDO_CTRL1 : TPS6507X_REG_DEFLDO2);
  		mask = (ldo == TPS6507X_LDO_1 ?
  			TPS6507X_REG_LDO_CTRL1_LDO1_MASK :
  				TPS6507X_REG_DEFLDO2_LDO2_MASK);
  	}
4ce5ba5ba   Todd Fischer   regulator: tps605...
421
  	data = tps6507x_pmic_reg_read(tps, reg);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
422
423
424
425
426
427
  	if (data < 0)
  		return data;
  
  	data &= mask;
  	return tps->info[ldo]->table[data] * 1000;
  }
4ce5ba5ba   Todd Fischer   regulator: tps605...
428
  static int tps6507x_pmic_ldo_set_voltage(struct regulator_dev *dev,
3a93f2a9f   Mark Brown   regulator: Report...
429
430
  					 int min_uV, int max_uV,
  					 unsigned *selector)
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
431
  {
4ce5ba5ba   Todd Fischer   regulator: tps605...
432
  	struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
  	int data, vsel, ldo = rdev_get_id(dev);
  	u8 reg, mask;
  
  	if (ldo < TPS6507X_LDO_1 || ldo > TPS6507X_LDO_2)
  		return -EINVAL;
  	else {
  		reg = (ldo == TPS6507X_LDO_1 ?
  			TPS6507X_REG_LDO_CTRL1 : TPS6507X_REG_DEFLDO2);
  		mask = (ldo == TPS6507X_LDO_1 ?
  			TPS6507X_REG_LDO_CTRL1_LDO1_MASK :
  				TPS6507X_REG_DEFLDO2_LDO2_MASK);
  	}
  
  	if (min_uV < tps->info[ldo]->min_uV || min_uV > tps->info[ldo]->max_uV)
  		return -EINVAL;
  	if (max_uV < tps->info[ldo]->min_uV || max_uV > tps->info[ldo]->max_uV)
  		return -EINVAL;
  
  	for (vsel = 0; vsel < tps->info[ldo]->table_len; vsel++) {
  		int mV = tps->info[ldo]->table[vsel];
  		int uV = mV * 1000;
  
  		/* Break at the first in-range value */
  		if (min_uV <= uV && uV <= max_uV)
  			break;
  	}
  
  	if (vsel == tps->info[ldo]->table_len)
  		return -EINVAL;
3a93f2a9f   Mark Brown   regulator: Report...
462
  	*selector = vsel;
4ce5ba5ba   Todd Fischer   regulator: tps605...
463
  	data = tps6507x_pmic_reg_read(tps, reg);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
464
465
466
467
468
  	if (data < 0)
  		return data;
  
  	data &= ~mask;
  	data |= vsel;
4ce5ba5ba   Todd Fischer   regulator: tps605...
469
  	return tps6507x_pmic_reg_write(tps, reg, data);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
470
  }
4ce5ba5ba   Todd Fischer   regulator: tps605...
471
  static int tps6507x_pmic_dcdc_list_voltage(struct regulator_dev *dev,
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
472
473
  					unsigned selector)
  {
4ce5ba5ba   Todd Fischer   regulator: tps605...
474
  	struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
475
476
477
478
479
480
481
482
483
484
  	int dcdc = rdev_get_id(dev);
  
  	if (dcdc < TPS6507X_DCDC_1 || dcdc > TPS6507X_DCDC_3)
  		return -EINVAL;
  
  	if (selector >= tps->info[dcdc]->table_len)
  		return -EINVAL;
  	else
  		return tps->info[dcdc]->table[selector] * 1000;
  }
4ce5ba5ba   Todd Fischer   regulator: tps605...
485
  static int tps6507x_pmic_ldo_list_voltage(struct regulator_dev *dev,
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
486
487
  					unsigned selector)
  {
4ce5ba5ba   Todd Fischer   regulator: tps605...
488
  	struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
489
490
491
492
493
494
495
496
497
498
499
500
  	int ldo = rdev_get_id(dev);
  
  	if (ldo < TPS6507X_LDO_1 || ldo > TPS6507X_LDO_2)
  		return -EINVAL;
  
  	if (selector >= tps->info[ldo]->table_len)
  		return -EINVAL;
  	else
  		return tps->info[ldo]->table[selector] * 1000;
  }
  
  /* Operations permitted on VDCDCx */
4ce5ba5ba   Todd Fischer   regulator: tps605...
501
502
503
504
505
506
507
  static struct regulator_ops tps6507x_pmic_dcdc_ops = {
  	.is_enabled = tps6507x_pmic_dcdc_is_enabled,
  	.enable = tps6507x_pmic_dcdc_enable,
  	.disable = tps6507x_pmic_dcdc_disable,
  	.get_voltage = tps6507x_pmic_dcdc_get_voltage,
  	.set_voltage = tps6507x_pmic_dcdc_set_voltage,
  	.list_voltage = tps6507x_pmic_dcdc_list_voltage,
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
508
509
510
  };
  
  /* Operations permitted on LDOx */
4ce5ba5ba   Todd Fischer   regulator: tps605...
511
512
513
514
515
516
517
  static struct regulator_ops tps6507x_pmic_ldo_ops = {
  	.is_enabled = tps6507x_pmic_ldo_is_enabled,
  	.enable = tps6507x_pmic_ldo_enable,
  	.disable = tps6507x_pmic_ldo_disable,
  	.get_voltage = tps6507x_pmic_ldo_get_voltage,
  	.set_voltage = tps6507x_pmic_ldo_set_voltage,
  	.list_voltage = tps6507x_pmic_ldo_list_voltage,
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
518
  };
31dd6a267   Todd Fischer   mfd: Add TPS6507x...
519
520
  static __devinit
  int tps6507x_pmic_probe(struct platform_device *pdev)
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
521
  {
31dd6a267   Todd Fischer   mfd: Add TPS6507x...
522
  	struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent);
7d14831e2   Anuj Aggarwal   regulator: tps650...
523
  	struct tps_info *info = &tps6507x_pmic_regs[0];
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
524
525
  	struct regulator_init_data *init_data;
  	struct regulator_dev *rdev;
4ce5ba5ba   Todd Fischer   regulator: tps605...
526
  	struct tps6507x_pmic *tps;
0bc20bba3   Todd Fischer   mfd: Add tps6507x...
527
  	struct tps6507x_board *tps_board;
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
528
  	int i;
56c23492b   Dmitry Torokhov   Regulators: tps65...
529
  	int error;
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
530

3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
531
  	/**
0bc20bba3   Todd Fischer   mfd: Add tps6507x...
532
533
534
  	 * tps_board points to pmic related constants
  	 * coming from the board-evm file.
  	 */
31dd6a267   Todd Fischer   mfd: Add TPS6507x...
535
  	tps_board = dev_get_platdata(tps6507x_dev->dev);
0bc20bba3   Todd Fischer   mfd: Add tps6507x...
536
537
538
539
  	if (!tps_board)
  		return -EINVAL;
  
  	/**
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
540
541
542
  	 * init_data points to array of regulator_init structures
  	 * coming from the board-evm file.
  	 */
0bc20bba3   Todd Fischer   mfd: Add tps6507x...
543
  	init_data = tps_board->tps6507x_pmic_init_data;
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
544
  	if (!init_data)
0bc20bba3   Todd Fischer   mfd: Add tps6507x...
545
  		return -EINVAL;
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
546
547
548
549
550
551
552
553
  
  	tps = kzalloc(sizeof(*tps), GFP_KERNEL);
  	if (!tps)
  		return -ENOMEM;
  
  	mutex_init(&tps->io_lock);
  
  	/* common for all regulators */
31dd6a267   Todd Fischer   mfd: Add TPS6507x...
554
  	tps->mfd = tps6507x_dev;
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
555
556
557
558
  
  	for (i = 0; i < TPS6507X_NUM_REGULATOR; i++, info++, init_data++) {
  		/* Register the regulators */
  		tps->info[i] = info;
7d14831e2   Anuj Aggarwal   regulator: tps650...
559
560
561
562
563
  		if (init_data->driver_data) {
  			struct tps6507x_reg_platform_data *data =
  							init_data->driver_data;
  			tps->info[i]->defdcdc_default = data->defdcdc_default;
  		}
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
564
  		tps->desc[i].name = info->name;
77fa44d0e   Axel Lin   regulator: Fix de...
565
  		tps->desc[i].id = i;
0fcdb109a   Axel Lin   regulator: tps650...
566
  		tps->desc[i].n_voltages = info->table_len;
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
567
  		tps->desc[i].ops = (i > TPS6507X_DCDC_3 ?
4ce5ba5ba   Todd Fischer   regulator: tps605...
568
  		&tps6507x_pmic_ldo_ops : &tps6507x_pmic_dcdc_ops);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
569
570
571
572
  		tps->desc[i].type = REGULATOR_VOLTAGE;
  		tps->desc[i].owner = THIS_MODULE;
  
  		rdev = regulator_register(&tps->desc[i],
2c043bcbf   Rajendra Nayak   regulator: pass a...
573
  					tps6507x_dev->dev, init_data, tps, NULL);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
574
  		if (IS_ERR(rdev)) {
31dd6a267   Todd Fischer   mfd: Add TPS6507x...
575
576
577
578
  			dev_err(tps6507x_dev->dev,
  				"failed to register %s regulator
  ",
  				pdev->name);
56c23492b   Dmitry Torokhov   Regulators: tps65...
579
580
  			error = PTR_ERR(rdev);
  			goto fail;
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
581
582
583
584
585
  		}
  
  		/* Save regulator for cleanup */
  		tps->rdev[i] = rdev;
  	}
31dd6a267   Todd Fischer   mfd: Add TPS6507x...
586
  	tps6507x_dev->pmic = tps;
d7399fa88   Axel Lin   regulator: tps650...
587
  	platform_set_drvdata(pdev, tps6507x_dev);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
588
589
  
  	return 0;
56c23492b   Dmitry Torokhov   Regulators: tps65...
590
591
592
593
594
595
596
  
  fail:
  	while (--i >= 0)
  		regulator_unregister(tps->rdev[i]);
  
  	kfree(tps);
  	return error;
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
597
  }
31dd6a267   Todd Fischer   mfd: Add TPS6507x...
598
  static int __devexit tps6507x_pmic_remove(struct platform_device *pdev)
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
599
  {
31dd6a267   Todd Fischer   mfd: Add TPS6507x...
600
601
  	struct tps6507x_dev *tps6507x_dev = platform_get_drvdata(pdev);
  	struct tps6507x_pmic *tps = tps6507x_dev->pmic;
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
602
603
604
605
  	int i;
  
  	for (i = 0; i < TPS6507X_NUM_REGULATOR; i++)
  		regulator_unregister(tps->rdev[i]);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
606
607
608
609
  	kfree(tps);
  
  	return 0;
  }
31dd6a267   Todd Fischer   mfd: Add TPS6507x...
610
  static struct platform_driver tps6507x_pmic_driver = {
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
611
  	.driver = {
31dd6a267   Todd Fischer   mfd: Add TPS6507x...
612
  		.name = "tps6507x-pmic",
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
613
614
  		.owner = THIS_MODULE,
  	},
4ce5ba5ba   Todd Fischer   regulator: tps605...
615
616
  	.probe = tps6507x_pmic_probe,
  	.remove = __devexit_p(tps6507x_pmic_remove),
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
617
618
619
  };
  
  /**
4ce5ba5ba   Todd Fischer   regulator: tps605...
620
   * tps6507x_pmic_init
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
621
622
623
   *
   * Module init function
   */
4ce5ba5ba   Todd Fischer   regulator: tps605...
624
  static int __init tps6507x_pmic_init(void)
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
625
  {
31dd6a267   Todd Fischer   mfd: Add TPS6507x...
626
  	return platform_driver_register(&tps6507x_pmic_driver);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
627
  }
4ce5ba5ba   Todd Fischer   regulator: tps605...
628
  subsys_initcall(tps6507x_pmic_init);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
629
630
  
  /**
4ce5ba5ba   Todd Fischer   regulator: tps605...
631
   * tps6507x_pmic_cleanup
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
632
633
634
   *
   * Module exit function
   */
4ce5ba5ba   Todd Fischer   regulator: tps605...
635
  static void __exit tps6507x_pmic_cleanup(void)
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
636
  {
31dd6a267   Todd Fischer   mfd: Add TPS6507x...
637
  	platform_driver_unregister(&tps6507x_pmic_driver);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
638
  }
4ce5ba5ba   Todd Fischer   regulator: tps605...
639
  module_exit(tps6507x_pmic_cleanup);
3fa5b8e08   Anuj Aggarwal   Regulator: Add TP...
640
641
642
  
  MODULE_AUTHOR("Texas Instruments");
  MODULE_DESCRIPTION("TPS6507x voltage regulator driver");
9e108d33e   Liam Girdwood   regulator: tps650...
643
  MODULE_LICENSE("GPL v2");
31dd6a267   Todd Fischer   mfd: Add TPS6507x...
644
  MODULE_ALIAS("platform:tps6507x-pmic");