aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/regulator/rc5t583-regulator.c
diff options
context:
space:
mode:
authorLaxman Dewangan <ldewangan@nvidia.com>2012-04-04 03:14:00 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2012-04-04 06:48:02 -0400
commit6ffc3270210efa2bea526953a142ffc908f5bd86 (patch)
treee05119524423cebeceb0f263901752decfed6fe5 /drivers/regulator/rc5t583-regulator.c
parent173f24d1ffe23f46b689159ee9d6b6aa402ff2e9 (diff)
regulator: Add support for RICOH PMIC RC5T583 regulator
The RC5T583 PMIC from RICOH consists of 4 DCDC and 10 LDOs. This driver supports the control of different regulator output through regulator interface. This driver depends on MFD driver of RC5T583 and uses mfd rc5t583 apis to communicate to device for accessing different device's registers. Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'drivers/regulator/rc5t583-regulator.c')
-rw-r--r--drivers/regulator/rc5t583-regulator.c367
1 files changed, 367 insertions, 0 deletions
diff --git a/drivers/regulator/rc5t583-regulator.c b/drivers/regulator/rc5t583-regulator.c
new file mode 100644
index 000000000000..37732f7c798d
--- /dev/null
+++ b/drivers/regulator/rc5t583-regulator.c
@@ -0,0 +1,367 @@
1/*
2 * Regulator driver for RICOH RC5T583 power management chip.
3 *
4 * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved.
5 * Author: Laxman dewangan <ldewangan@nvidia.com>
6 *
7 * based on code
8 * Copyright (C) 2011 RICOH COMPANY,LTD
9 *
10 *
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms and conditions of the GNU General Public License,
13 * version 2, as published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18 * more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 *
23 */
24
25#include <linux/module.h>
26#include <linux/delay.h>
27#include <linux/init.h>
28#include <linux/slab.h>
29#include <linux/err.h>
30#include <linux/platform_device.h>
31#include <linux/regulator/driver.h>
32#include <linux/regulator/machine.h>
33#include <linux/gpio.h>
34#include <linux/mfd/rc5t583.h>
35
36struct rc5t583_regulator_info {
37 int deepsleep_id;
38
39 /* Regulator register address.*/
40 uint8_t reg_en_reg;
41 uint8_t en_bit;
42 uint8_t reg_disc_reg;
43 uint8_t disc_bit;
44 uint8_t vout_reg;
45 uint8_t vout_mask;
46 uint8_t deepsleep_reg;
47
48 /* Chip constraints on regulator behavior */
49 int min_uV;
50 int max_uV;
51 int step_uV;
52 int nsteps;
53
54 /* Regulator specific turn-on delay and voltage settling time*/
55 int enable_uv_per_us;
56 int change_uv_per_us;
57
58 /* Used by regulator core */
59 struct regulator_desc desc;
60};
61
62struct rc5t583_regulator {
63 struct rc5t583_regulator_info *reg_info;
64
65 /* Devices */
66 struct device *dev;
67 struct rc5t583 *mfd;
68 struct regulator_dev *rdev;
69};
70
71static int rc5t583_reg_is_enabled(struct regulator_dev *rdev)
72{
73 struct rc5t583_regulator *reg = rdev_get_drvdata(rdev);
74 struct rc5t583_regulator_info *ri = reg->reg_info;
75 uint8_t control;
76 int ret;
77
78 ret = rc5t583_read(reg->mfd->dev, ri->reg_en_reg, &control);
79 if (ret < 0) {
80 dev_err(&rdev->dev,
81 "Error in reading the control register 0x%02x\n",
82 ri->reg_en_reg);
83 return ret;
84 }
85 return !!(control & BIT(ri->en_bit));
86}
87
88static int rc5t583_reg_enable(struct regulator_dev *rdev)
89{
90 struct rc5t583_regulator *reg = rdev_get_drvdata(rdev);
91 struct rc5t583_regulator_info *ri = reg->reg_info;
92 int ret;
93
94 ret = rc5t583_set_bits(reg->mfd->dev, ri->reg_en_reg,
95 (1 << ri->en_bit));
96 if (ret < 0) {
97 dev_err(&rdev->dev,
98 "Error in setting bit of STATE register 0x%02x\n",
99 ri->reg_en_reg);
100 return ret;
101 }
102 return ret;
103}
104
105static int rc5t583_reg_disable(struct regulator_dev *rdev)
106{
107 struct rc5t583_regulator *reg = rdev_get_drvdata(rdev);
108 struct rc5t583_regulator_info *ri = reg->reg_info;
109 int ret;
110
111 ret = rc5t583_clear_bits(reg->mfd->dev, ri->reg_en_reg,
112 (1 << ri->en_bit));
113 if (ret < 0)
114 dev_err(&rdev->dev,
115 "Error in clearing bit of STATE register 0x%02x\n",
116 ri->reg_en_reg);
117
118 return ret;
119}
120
121static int rc5t583_list_voltage(struct regulator_dev *rdev, unsigned selector)
122{
123 struct rc5t583_regulator *reg = rdev_get_drvdata(rdev);
124 struct rc5t583_regulator_info *ri = reg->reg_info;
125 return ri->min_uV + (ri->step_uV * selector);
126}
127
128static int rc5t583_set_voltage_sel(struct regulator_dev *rdev,
129 unsigned int selector)
130{
131 struct rc5t583_regulator *reg = rdev_get_drvdata(rdev);
132 struct rc5t583_regulator_info *ri = reg->reg_info;
133 int ret;
134 if (selector > ri->nsteps) {
135 dev_err(&rdev->dev, "Invalid selector 0x%02x\n", selector);
136 return -EINVAL;
137 }
138
139 ret = rc5t583_update(reg->mfd->dev, ri->vout_reg,
140 selector, ri->vout_mask);
141 if (ret < 0)
142 dev_err(&rdev->dev,
143 "Error in update voltage register 0x%02x\n", ri->vout_reg);
144 return ret;
145}
146
147static int rc5t583_get_voltage_sel(struct regulator_dev *rdev)
148{
149 struct rc5t583_regulator *reg = rdev_get_drvdata(rdev);
150 struct rc5t583_regulator_info *ri = reg->reg_info;
151 uint8_t vsel;
152 int ret;
153 ret = rc5t583_read(reg->mfd->dev, ri->vout_reg, &vsel);
154 if (ret < 0) {
155 dev_err(&rdev->dev,
156 "Error in reading voltage register 0x%02x\n", ri->vout_reg);
157 return ret;
158 }
159 return vsel & ri->vout_mask;
160}
161
162static int rc5t583_regulator_enable_time(struct regulator_dev *rdev)
163{
164 struct rc5t583_regulator *reg = rdev_get_drvdata(rdev);
165 int vsel = rc5t583_get_voltage_sel(rdev);
166 int curr_uV = rc5t583_list_voltage(rdev, vsel);
167 return DIV_ROUND_UP(curr_uV, reg->reg_info->enable_uv_per_us);
168}
169
170static int rc5t583_set_voltage_time_sel(struct regulator_dev *rdev,
171 unsigned int old_selector, unsigned int new_selector)
172{
173 struct rc5t583_regulator *reg = rdev_get_drvdata(rdev);
174 int old_uV, new_uV;
175 old_uV = rc5t583_list_voltage(rdev, old_selector);
176
177 if (old_uV < 0)
178 return old_uV;
179
180 new_uV = rc5t583_list_voltage(rdev, new_selector);
181 if (new_uV < 0)
182 return new_uV;
183
184 return DIV_ROUND_UP(abs(old_uV - new_uV),
185 reg->reg_info->change_uv_per_us);
186}
187
188
189static struct regulator_ops rc5t583_ops = {
190 .is_enabled = rc5t583_reg_is_enabled,
191 .enable = rc5t583_reg_enable,
192 .disable = rc5t583_reg_disable,
193 .enable_time = rc5t583_regulator_enable_time,
194 .get_voltage_sel = rc5t583_get_voltage_sel,
195 .set_voltage_sel = rc5t583_set_voltage_sel,
196 .list_voltage = rc5t583_list_voltage,
197 .set_voltage_time_sel = rc5t583_set_voltage_time_sel,
198};
199
200#define RC5T583_REG(_id, _en_reg, _en_bit, _disc_reg, _disc_bit, _vout_reg, \
201 _vout_mask, _ds_reg, _min_mv, _max_mv, _step_uV, _nsteps, \
202 _enable_mv) \
203{ \
204 .reg_en_reg = RC5T583_REG_##_en_reg, \
205 .en_bit = _en_bit, \
206 .reg_disc_reg = RC5T583_REG_##_disc_reg, \
207 .disc_bit = _disc_bit, \
208 .vout_reg = RC5T583_REG_##_vout_reg, \
209 .vout_mask = _vout_mask, \
210 .deepsleep_reg = RC5T583_REG_##_ds_reg, \
211 .min_uV = _min_mv * 1000, \
212 .max_uV = _max_mv * 1000, \
213 .step_uV = _step_uV, \
214 .nsteps = _nsteps, \
215 .enable_uv_per_us = _enable_mv * 1000, \
216 .change_uv_per_us = 40 * 1000, \
217 .deepsleep_id = RC5T583_DS_##_id, \
218 .desc = { \
219 .name = "rc5t583-regulator-"#_id, \
220 .id = RC5T583_REGULATOR_##_id, \
221 .n_voltages = _nsteps, \
222 .ops = &rc5t583_ops, \
223 .type = REGULATOR_VOLTAGE, \
224 .owner = THIS_MODULE, \
225 }, \
226}
227
228static struct rc5t583_regulator_info rc5t583_reg_info[RC5T583_REGULATOR_MAX] = {
229 RC5T583_REG(DC0, DC0CTL, 0, DC0CTL, 1, DC0DAC, 0x7F, DC0DAC_DS,
230 700, 1500, 12500, 0x41, 4),
231 RC5T583_REG(DC1, DC1CTL, 0, DC1CTL, 1, DC1DAC, 0x7F, DC1DAC_DS,
232 700, 1500, 12500, 0x41, 14),
233 RC5T583_REG(DC2, DC2CTL, 0, DC2CTL, 1, DC2DAC, 0x7F, DC2DAC_DS,
234 900, 2400, 12500, 0x79, 14),
235 RC5T583_REG(DC3, DC3CTL, 0, DC3CTL, 1, DC3DAC, 0x7F, DC3DAC_DS,
236 900, 2400, 12500, 0x79, 14),
237 RC5T583_REG(LDO0, LDOEN2, 0, LDODIS2, 0, LDO0DAC, 0x7F, LDO0DAC_DS,
238 900, 3400, 25000, 0x65, 160),
239 RC5T583_REG(LDO1, LDOEN2, 1, LDODIS2, 1, LDO1DAC, 0x7F, LDO1DAC_DS,
240 900, 3400, 25000, 0x65, 160),
241 RC5T583_REG(LDO2, LDOEN2, 2, LDODIS2, 2, LDO2DAC, 0x7F, LDO2DAC_DS,
242 900, 3400, 25000, 0x65, 160),
243 RC5T583_REG(LDO3, LDOEN2, 3, LDODIS2, 3, LDO3DAC, 0x7F, LDO3DAC_DS,
244 900, 3400, 25000, 0x65, 160),
245 RC5T583_REG(LDO4, LDOEN2, 4, LDODIS2, 4, LDO4DAC, 0x3F, LDO4DAC_DS,
246 750, 1500, 12500, 0x3D, 133),
247 RC5T583_REG(LDO5, LDOEN2, 5, LDODIS2, 5, LDO5DAC, 0x7F, LDO5DAC_DS,
248 900, 3400, 25000, 0x65, 267),
249 RC5T583_REG(LDO6, LDOEN2, 6, LDODIS2, 6, LDO6DAC, 0x7F, LDO6DAC_DS,
250 900, 3400, 25000, 0x65, 133),
251 RC5T583_REG(LDO7, LDOEN2, 7, LDODIS2, 7, LDO7DAC, 0x7F, LDO7DAC_DS,
252 900, 3400, 25000, 0x65, 233),
253 RC5T583_REG(LDO8, LDOEN1, 0, LDODIS1, 0, LDO8DAC, 0x7F, LDO8DAC_DS,
254 900, 3400, 25000, 0x65, 233),
255 RC5T583_REG(LDO9, LDOEN1, 1, LDODIS1, 1, LDO9DAC, 0x7F, LDO9DAC_DS,
256 900, 3400, 25000, 0x65, 133),
257};
258
259static int __devinit rc5t583_regulator_probe(struct platform_device *pdev)
260{
261 struct rc5t583 *rc5t583 = dev_get_drvdata(pdev->dev.parent);
262 struct rc5t583_platform_data *pdata = dev_get_platdata(rc5t583->dev);
263 struct regulator_init_data *reg_data;
264 struct rc5t583_regulator *reg = NULL;
265 struct rc5t583_regulator *regs;
266 struct regulator_dev *rdev;
267 struct rc5t583_regulator_info *ri;
268 int ret;
269 int id;
270
271 if (!pdata) {
272 dev_err(&pdev->dev, "No platform data, exiting...\n");
273 return -ENODEV;
274 }
275
276 regs = devm_kzalloc(&pdev->dev, RC5T583_REGULATOR_MAX *
277 sizeof(struct rc5t583_regulator), GFP_KERNEL);
278 if (!regs) {
279 dev_err(&pdev->dev, "Memory allocation failed exiting..\n");
280 return -ENOMEM;
281 }
282
283
284 for (id = 0; id < RC5T583_REGULATOR_MAX; ++id) {
285 reg_data = pdata->reg_init_data[id];
286
287 /* No need to register if there is no regulator data */
288 if (!reg_data)
289 continue;
290
291 reg = &regs[id];
292 ri = &rc5t583_reg_info[id];
293 reg->reg_info = ri;
294 reg->mfd = rc5t583;
295 reg->dev = &pdev->dev;
296
297 if (ri->deepsleep_id == RC5T583_DS_NONE)
298 goto skip_ext_pwr_config;
299
300 ret = rc5t583_ext_power_req_config(rc5t583->dev,
301 ri->deepsleep_id,
302 pdata->regulator_ext_pwr_control[id],
303 pdata->regulator_deepsleep_slot[id]);
304 /*
305 * Configuring external control is not a major issue,
306 * just give warning.
307 */
308 if (ret < 0)
309 dev_warn(&pdev->dev,
310 "Failed to configure ext control %d\n", id);
311
312skip_ext_pwr_config:
313 rdev = regulator_register(&ri->desc, &pdev->dev,
314 reg_data, reg, NULL);
315 if (IS_ERR_OR_NULL(rdev)) {
316 dev_err(&pdev->dev, "Failed to register regulator %s\n",
317 ri->desc.name);
318 ret = PTR_ERR(rdev);
319 goto clean_exit;
320 }
321 reg->rdev = rdev;
322 }
323 platform_set_drvdata(pdev, regs);
324 return 0;
325
326clean_exit:
327 while (--id > 0)
328 regulator_unregister(regs[id].rdev);
329
330 return ret;
331}
332
333static int __devexit rc5t583_regulator_remove(struct platform_device *pdev)
334{
335 struct rc5t583_regulator *regs = platform_get_drvdata(pdev);
336 int id;
337
338 for (id = 0; id < RC5T583_REGULATOR_MAX; ++id)
339 regulator_unregister(regs[id].rdev);
340 return 0;
341}
342
343static struct platform_driver rc5t583_regulator_driver = {
344 .driver = {
345 .name = "rc5t583-regulator",
346 .owner = THIS_MODULE,
347 },
348 .probe = rc5t583_regulator_probe,
349 .remove = __devexit_p(rc5t583_regulator_remove),
350};
351
352static int __init rc5t583_regulator_init(void)
353{
354 return platform_driver_register(&rc5t583_regulator_driver);
355}
356subsys_initcall(rc5t583_regulator_init);
357
358static void __exit rc5t583_regulator_exit(void)
359{
360 platform_driver_unregister(&rc5t583_regulator_driver);
361}
362module_exit(rc5t583_regulator_exit);
363
364MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
365MODULE_DESCRIPTION("RC5T583 regulator driver");
366MODULE_ALIAS("platform:rc5t583-regulator");
367MODULE_LICENSE("GPL V2");