regulator: add a regulator driver for the AS3711 PMIC
[deliverable/linux.git] / drivers / regulator / as3711-regulator.c
CommitLineData
f1e64f90
GL
1/*
2 * AS3711 PMIC regulator driver, using DCDC Step Down and LDO supplies
3 *
4 * Copyright (C) 2012 Renesas Electronics Corporation
5 * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the version 2 of the GNU General Public License as
9 * published by the Free Software Foundation
10 */
11
12#include <linux/err.h>
13#include <linux/init.h>
14#include <linux/mfd/as3711.h>
15#include <linux/module.h>
16#include <linux/platform_device.h>
17#include <linux/regmap.h>
18#include <linux/regulator/driver.h>
19#include <linux/slab.h>
20
21struct as3711_regulator_info {
22 struct regulator_desc desc;
23 unsigned int max_uV;
24};
25
26struct as3711_regulator {
27 struct as3711_regulator_info *reg_info;
28 struct regulator_dev *rdev;
29};
30
31static int as3711_list_voltage_sd(struct regulator_dev *rdev,
32 unsigned int selector)
33{
34 if (selector >= rdev->desc->n_voltages)
35 return -EINVAL;
36
37 if (!selector)
38 return 0;
39 if (selector < 0x41)
40 return 600000 + selector * 12500;
41 if (selector < 0x71)
42 return 1400000 + (selector - 0x40) * 25000;
43 return 2600000 + (selector - 0x70) * 50000;
44}
45
46static int as3711_list_voltage_aldo(struct regulator_dev *rdev,
47 unsigned int selector)
48{
49 if (selector >= rdev->desc->n_voltages)
50 return -EINVAL;
51
52 if (selector < 0x10)
53 return 1200000 + selector * 50000;
54 return 1800000 + (selector - 0x10) * 100000;
55}
56
57static int as3711_list_voltage_dldo(struct regulator_dev *rdev,
58 unsigned int selector)
59{
60 if (selector >= rdev->desc->n_voltages ||
61 (selector > 0x10 && selector < 0x20))
62 return -EINVAL;
63
64 if (selector < 0x11)
65 return 900000 + selector * 50000;
66 return 1750000 + (selector - 0x20) * 50000;
67}
68
69static int as3711_bound_check(struct regulator_dev *rdev,
70 int *min_uV, int *max_uV)
71{
72 struct as3711_regulator_info *info = container_of(rdev->desc,
73 struct as3711_regulator_info, desc);
74 struct as3711_regulator *reg = rdev->reg_data;
75
76 WARN_ON(reg->reg_info != info);
77
78 dev_dbg(&rdev->dev, "%s(), %d, %d, %d\n", __func__,
79 *min_uV, rdev->desc->min_uV, info->max_uV);
80
81 if (*max_uV < *min_uV ||
82 *min_uV >= info->max_uV || rdev->desc->min_uV >= *max_uV)
83 return -EINVAL;
84
85 if (rdev->desc->n_voltages == 1)
86 return 0;
87
88 if (*max_uV > info->max_uV)
89 *max_uV = info->max_uV;
90
91 if (*min_uV < rdev->desc->min_uV)
92 *min_uV = rdev->desc->min_uV;
93
94 return *min_uV;
95}
96
97static int as3711_sel_check(int min, int max, int bottom, int step)
98{
99 int ret, voltage;
100
101 /* Round up min, when dividing: keeps us within the range */
102 ret = (min - bottom + step - 1) / step;
103 voltage = ret * step + bottom;
104 pr_debug("%s(): select %d..%d in %d+N*%d: %d\n", __func__,
105 min, max, bottom, step, ret);
106 if (voltage > max) {
107 /*
108 * Try 1 down. It will take us below min, but as long we stay
109 * above bottom, we're fine.
110 */
111 ret--;
112 voltage = ret * step + bottom;
113 if (voltage < bottom)
114 return -EINVAL;
115 }
116 return ret;
117}
118
119static int as3711_map_voltage_sd(struct regulator_dev *rdev,
120 int min_uV, int max_uV)
121{
122 int ret;
123
124 ret = as3711_bound_check(rdev, &min_uV, &max_uV);
125 if (ret <= 0)
126 return ret;
127
128 if (min_uV <= 1400000)
129 return as3711_sel_check(min_uV, max_uV, 600000, 12500);
130
131 if (min_uV <= 2600000)
132 return as3711_sel_check(min_uV, max_uV, 1400000, 25000) + 0x40;
133
134 return as3711_sel_check(min_uV, max_uV, 2600000, 50000) + 0x70;
135}
136
137/*
138 * The regulator API supports 4 modes of operataion: FAST, NORMAL, IDLE and
139 * STANDBY. We map them in the following way to AS3711 SD1-4 DCDC modes:
140 * FAST: sdX_fast=1
141 * NORMAL: low_noise=1
142 * IDLE: low_noise=0
143 */
144
145static int as3711_set_mode_sd(struct regulator_dev *rdev, unsigned int mode)
146{
147 unsigned int fast_bit = rdev->desc->enable_mask,
148 low_noise_bit = fast_bit << 4;
149 u8 val;
150
151 switch (mode) {
152 case REGULATOR_MODE_FAST:
153 val = fast_bit | low_noise_bit;
154 break;
155 case REGULATOR_MODE_NORMAL:
156 val = low_noise_bit;
157 break;
158 case REGULATOR_MODE_IDLE:
159 val = 0;
160 break;
161 default:
162 return -EINVAL;
163 }
164
165 return regmap_update_bits(rdev->regmap, AS3711_SD_CONTROL_1,
166 low_noise_bit | fast_bit, val);
167}
168
169static unsigned int as3711_get_mode_sd(struct regulator_dev *rdev)
170{
171 unsigned int fast_bit = rdev->desc->enable_mask,
172 low_noise_bit = fast_bit << 4, mask = fast_bit | low_noise_bit;
173 unsigned int val;
174 int ret = regmap_read(rdev->regmap, AS3711_SD_CONTROL_1, &val);
175
176 if (ret < 0)
177 return ret;
178
179 if ((val & mask) == mask)
180 return REGULATOR_MODE_FAST;
181
182 if ((val & mask) == low_noise_bit)
183 return REGULATOR_MODE_NORMAL;
184
185 if (!(val & mask))
186 return REGULATOR_MODE_IDLE;
187
188 return -EINVAL;
189}
190
191static int as3711_map_voltage_aldo(struct regulator_dev *rdev,
192 int min_uV, int max_uV)
193{
194 int ret;
195
196 ret = as3711_bound_check(rdev, &min_uV, &max_uV);
197 if (ret <= 0)
198 return ret;
199
200 if (min_uV <= 1800000)
201 return as3711_sel_check(min_uV, max_uV, 1200000, 50000);
202
203 return as3711_sel_check(min_uV, max_uV, 1800000, 100000) + 0x10;
204}
205
206static int as3711_map_voltage_dldo(struct regulator_dev *rdev,
207 int min_uV, int max_uV)
208{
209 int ret;
210
211 ret = as3711_bound_check(rdev, &min_uV, &max_uV);
212 if (ret <= 0)
213 return ret;
214
215 if (min_uV <= 1700000)
216 return as3711_sel_check(min_uV, max_uV, 900000, 50000);
217
218 return as3711_sel_check(min_uV, max_uV, 1750000, 50000) + 0x20;
219}
220
221static struct regulator_ops as3711_sd_ops = {
222 .is_enabled = regulator_is_enabled_regmap,
223 .enable = regulator_enable_regmap,
224 .disable = regulator_disable_regmap,
225 .get_voltage_sel = regulator_get_voltage_sel_regmap,
226 .set_voltage_sel = regulator_set_voltage_sel_regmap,
227 .list_voltage = as3711_list_voltage_sd,
228 .map_voltage = as3711_map_voltage_sd,
229 .get_mode = as3711_get_mode_sd,
230 .set_mode = as3711_set_mode_sd,
231};
232
233static struct regulator_ops as3711_aldo_ops = {
234 .is_enabled = regulator_is_enabled_regmap,
235 .enable = regulator_enable_regmap,
236 .disable = regulator_disable_regmap,
237 .get_voltage_sel = regulator_get_voltage_sel_regmap,
238 .set_voltage_sel = regulator_set_voltage_sel_regmap,
239 .list_voltage = as3711_list_voltage_aldo,
240 .map_voltage = as3711_map_voltage_aldo,
241};
242
243static struct regulator_ops as3711_dldo_ops = {
244 .is_enabled = regulator_is_enabled_regmap,
245 .enable = regulator_enable_regmap,
246 .disable = regulator_disable_regmap,
247 .get_voltage_sel = regulator_get_voltage_sel_regmap,
248 .set_voltage_sel = regulator_set_voltage_sel_regmap,
249 .list_voltage = as3711_list_voltage_dldo,
250 .map_voltage = as3711_map_voltage_dldo,
251};
252
253#define AS3711_REG(_id, _en_reg, _en_bit, _vmask, _vshift, _min_uV, _max_uV, _sfx) \
254 [AS3711_REGULATOR_ ## _id] = { \
255 .desc = { \
256 .name = "as3711-regulator-" # _id, \
257 .id = AS3711_REGULATOR_ ## _id, \
258 .n_voltages = (_vmask + 1), \
259 .ops = &as3711_ ## _sfx ## _ops, \
260 .type = REGULATOR_VOLTAGE, \
261 .owner = THIS_MODULE, \
262 .vsel_reg = AS3711_ ## _id ## _VOLTAGE, \
263 .vsel_mask = _vmask << _vshift, \
264 .enable_reg = AS3711_ ## _en_reg, \
265 .enable_mask = BIT(_en_bit), \
266 .min_uV = _min_uV, \
267 }, \
268 .max_uV = _max_uV, \
269}
270
271static struct as3711_regulator_info as3711_reg_info[] = {
272 AS3711_REG(SD_1, SD_CONTROL, 0, 0x7f, 0, 612500, 3350000, sd),
273 AS3711_REG(SD_2, SD_CONTROL, 1, 0x7f, 0, 612500, 3350000, sd),
274 AS3711_REG(SD_3, SD_CONTROL, 2, 0x7f, 0, 612500, 3350000, sd),
275 AS3711_REG(SD_4, SD_CONTROL, 3, 0x7f, 0, 612500, 3350000, sd),
276 AS3711_REG(LDO_1, LDO_1_VOLTAGE, 7, 0x1f, 0, 1200000, 3300000, aldo),
277 AS3711_REG(LDO_2, LDO_2_VOLTAGE, 7, 0x1f, 0, 1200000, 3300000, aldo),
278 AS3711_REG(LDO_3, LDO_3_VOLTAGE, 7, 0x3f, 0, 900000, 3300000, dldo),
279 AS3711_REG(LDO_4, LDO_4_VOLTAGE, 7, 0x3f, 0, 900000, 3300000, dldo),
280 AS3711_REG(LDO_5, LDO_5_VOLTAGE, 7, 0x3f, 0, 900000, 3300000, dldo),
281 AS3711_REG(LDO_6, LDO_6_VOLTAGE, 7, 0x3f, 0, 900000, 3300000, dldo),
282 AS3711_REG(LDO_7, LDO_7_VOLTAGE, 7, 0x3f, 0, 900000, 3300000, dldo),
283 AS3711_REG(LDO_8, LDO_8_VOLTAGE, 7, 0x3f, 0, 900000, 3300000, dldo),
284 /* StepUp output voltage depends on supplying regulator */
285};
286
287#define AS3711_REGULATOR_NUM ARRAY_SIZE(as3711_reg_info)
288
289static int as3711_regulator_probe(struct platform_device *pdev)
290{
291 struct as3711_regulator_pdata *pdata = dev_get_platdata(&pdev->dev);
292 struct as3711 *as3711 = dev_get_drvdata(pdev->dev.parent);
293 struct regulator_init_data *reg_data;
294 struct regulator_config config = {.dev = &pdev->dev,};
295 struct as3711_regulator *reg = NULL;
296 struct as3711_regulator *regs;
297 struct regulator_dev *rdev;
298 struct as3711_regulator_info *ri;
299 int ret;
300 int id;
301
302 if (!pdata)
303 dev_dbg(&pdev->dev, "No platform data...\n");
304
305 regs = devm_kzalloc(&pdev->dev, AS3711_REGULATOR_NUM *
306 sizeof(struct as3711_regulator), GFP_KERNEL);
307 if (!regs) {
308 dev_err(&pdev->dev, "Memory allocation failed exiting..\n");
309 return -ENOMEM;
310 }
311
312 for (id = 0, ri = as3711_reg_info; id < AS3711_REGULATOR_NUM; ++id, ri++) {
313 reg_data = pdata ? pdata->init_data[id] : NULL;
314
315 /* No need to register if there is no regulator data */
316 if (!ri->desc.name)
317 continue;
318
319 reg = &regs[id];
320 reg->reg_info = ri;
321
322 config.init_data = reg_data;
323 config.driver_data = reg;
324 config.regmap = as3711->regmap;
325
326 rdev = regulator_register(&ri->desc, &config);
327 if (IS_ERR(rdev)) {
328 dev_err(&pdev->dev, "Failed to register regulator %s\n",
329 ri->desc.name);
330 ret = PTR_ERR(rdev);
331 goto eregreg;
332 }
333 reg->rdev = rdev;
334 }
335 platform_set_drvdata(pdev, regs);
336 return 0;
337
338eregreg:
339 while (--id >= 0)
340 regulator_unregister(regs[id].rdev);
341
342 return ret;
343}
344
345static int as3711_regulator_remove(struct platform_device *pdev)
346{
347 struct as3711_regulator *regs = platform_get_drvdata(pdev);
348 int id;
349
350 for (id = 0; id < AS3711_REGULATOR_NUM; ++id)
351 regulator_unregister(regs[id].rdev);
352 return 0;
353}
354
355static struct platform_driver as3711_regulator_driver = {
356 .driver = {
357 .name = "as3711-regulator",
358 .owner = THIS_MODULE,
359 },
360 .probe = as3711_regulator_probe,
361 .remove = as3711_regulator_remove,
362};
363
364static int __init as3711_regulator_init(void)
365{
366 return platform_driver_register(&as3711_regulator_driver);
367}
368subsys_initcall(as3711_regulator_init);
369
370static void __exit as3711_regulator_exit(void)
371{
372 platform_driver_unregister(&as3711_regulator_driver);
373}
374module_exit(as3711_regulator_exit);
375
376MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
377MODULE_DESCRIPTION("AS3711 regulator driver");
378MODULE_ALIAS("platform:as3711-regulator");
379MODULE_LICENSE("GPL v2");
This page took 0.047218 seconds and 5 git commands to generate.