blob: 9893a6b5f3dbdff8d37543e5e8617a33afd0cc8a [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Przemyslaw Marczak5d387d02015-05-13 13:38:32 +02002/*
3 * Copyright (C) 2015 Samsung Electronics
4 * Przemyslaw Marczak <p.marczak@samsung.com>
Przemyslaw Marczak5d387d02015-05-13 13:38:32 +02005 */
6
7#include <common.h>
8#include <fdtdec.h>
9#include <errno.h>
10#include <dm.h>
11#include <i2c.h>
12#include <power/pmic.h>
13#include <power/regulator.h>
14#include <power/sandbox_pmic.h>
15
Przemyslaw Marczak5d387d02015-05-13 13:38:32 +020016#define MODE(_id, _val, _name) [_id] = { \
17 .id = _id, \
18 .register_value = _val, \
19 .name = _name, \
20}
21
22#define RANGE(_min, _max, _step) { \
23 .min = _min, \
24 .max = _max, \
25 .step = _step, \
26}
27
28/*
29 * struct output_range - helper structure type to define the range of output
30 * operating values (current/voltage), limited by the PMIC IC design.
31 *
32 * @min - minimum value
33 * @max - maximum value
34 * @step - step value
35*/
36struct output_range {
37 int min;
38 int max;
39 int step;
40};
41
42/* BUCK: 1,2 - voltage range */
43static struct output_range buck_voltage_range[] = {
44 RANGE(OUT_BUCK1_UV_MIN, OUT_BUCK1_UV_MAX, OUT_BUCK1_UV_STEP),
45 RANGE(OUT_BUCK2_UV_MIN, OUT_BUCK2_UV_MAX, OUT_BUCK2_UV_STEP),
46};
47
48/* BUCK: 1 - current range */
49static struct output_range buck_current_range[] = {
50 RANGE(OUT_BUCK1_UA_MIN, OUT_BUCK1_UA_MAX, OUT_BUCK1_UA_STEP),
51};
52
53/* BUCK operating modes */
54static struct dm_regulator_mode sandbox_buck_modes[] = {
55 MODE(BUCK_OM_OFF, OM2REG(BUCK_OM_OFF), "OFF"),
56 MODE(BUCK_OM_ON, OM2REG(BUCK_OM_ON), "ON"),
57 MODE(BUCK_OM_PWM, OM2REG(BUCK_OM_PWM), "PWM"),
58};
59
60/* LDO: 1,2 - voltage range */
61static struct output_range ldo_voltage_range[] = {
62 RANGE(OUT_LDO1_UV_MIN, OUT_LDO1_UV_MAX, OUT_LDO1_UV_STEP),
63 RANGE(OUT_LDO2_UV_MIN, OUT_LDO2_UV_MAX, OUT_LDO2_UV_STEP),
64};
65
66/* LDO: 1 - current range */
67static struct output_range ldo_current_range[] = {
68 RANGE(OUT_LDO1_UA_MIN, OUT_LDO1_UA_MAX, OUT_LDO1_UA_STEP),
69};
70
71/* LDO operating modes */
72static struct dm_regulator_mode sandbox_ldo_modes[] = {
73 MODE(LDO_OM_OFF, OM2REG(LDO_OM_OFF), "OFF"),
74 MODE(LDO_OM_ON, OM2REG(LDO_OM_ON), "ON"),
75 MODE(LDO_OM_SLEEP, OM2REG(LDO_OM_SLEEP), "SLEEP"),
76 MODE(LDO_OM_STANDBY, OM2REG(LDO_OM_STANDBY), "STANDBY"),
77};
78
79int out_get_value(struct udevice *dev, int output_count, int reg_type,
80 struct output_range *range)
81{
82 uint8_t reg_val;
83 uint reg;
84 int ret;
85
86 if (dev->driver_data > output_count) {
Masahiro Yamada9b643e32017-09-16 14:10:41 +090087 pr_err("Unknown regulator number: %lu for PMIC %s!",
Przemyslaw Marczak5d387d02015-05-13 13:38:32 +020088 dev->driver_data, dev->name);
89 return -EINVAL;
90 }
91
92 reg = (dev->driver_data - 1) * OUT_REG_COUNT + reg_type;
93 ret = pmic_read(dev->parent, reg, &reg_val, 1);
94 if (ret) {
Masahiro Yamada9b643e32017-09-16 14:10:41 +090095 pr_err("PMIC read failed: %d\n", ret);
Przemyslaw Marczak5d387d02015-05-13 13:38:32 +020096 return ret;
97 }
98
99 ret = REG2VAL(range[dev->driver_data - 1].min,
100 range[dev->driver_data - 1].step,
101 reg_val);
102
103 return ret;
104}
105
106static int out_set_value(struct udevice *dev, int output_count, int reg_type,
107 struct output_range *range, int value)
108{
109 uint8_t reg_val;
110 uint reg;
111 int ret;
112 int max_value;
113
114 if (dev->driver_data > output_count) {
Masahiro Yamada9b643e32017-09-16 14:10:41 +0900115 pr_err("Unknown regulator number: %lu for PMIC %s!",
Przemyslaw Marczak5d387d02015-05-13 13:38:32 +0200116 dev->driver_data, dev->name);
117 return -EINVAL;
118 }
119
120 max_value = range[dev->driver_data - 1].max;
121 if (value > max_value) {
Masahiro Yamada9b643e32017-09-16 14:10:41 +0900122 pr_err("Wrong value for %s: %lu. Max is: %d.",
Przemyslaw Marczak5d387d02015-05-13 13:38:32 +0200123 dev->name, dev->driver_data, max_value);
124 return -EINVAL;
125 }
126
127 reg_val = VAL2REG(range[dev->driver_data - 1].min,
128 range[dev->driver_data - 1].step,
129 value);
130
131 reg = (dev->driver_data - 1) * OUT_REG_COUNT + reg_type;
132 ret = pmic_write(dev->parent, reg, &reg_val, 1);
133 if (ret) {
Masahiro Yamada9b643e32017-09-16 14:10:41 +0900134 pr_err("PMIC write failed: %d\n", ret);
Przemyslaw Marczak5d387d02015-05-13 13:38:32 +0200135 return ret;
136 }
137
138 return 0;
139}
140
141static int out_get_mode(struct udevice *dev)
142{
143 struct dm_regulator_uclass_platdata *uc_pdata;
144 uint8_t reg_val;
145 uint reg;
146 int ret;
147 int i;
148
149 uc_pdata = dev_get_uclass_platdata(dev);
150
151 reg = (dev->driver_data - 1) * OUT_REG_COUNT + OUT_REG_OM;
152 ret = pmic_read(dev->parent, reg, &reg_val, 1);
153 if (ret) {
Masahiro Yamada9b643e32017-09-16 14:10:41 +0900154 pr_err("PMIC read failed: %d\n", ret);
Przemyslaw Marczak5d387d02015-05-13 13:38:32 +0200155 return ret;
156 }
157
158 for (i = 0; i < uc_pdata->mode_count; i++) {
159 if (reg_val == uc_pdata->mode[i].register_value)
160 return uc_pdata->mode[i].id;
161 }
162
Masahiro Yamada9b643e32017-09-16 14:10:41 +0900163 pr_err("Unknown operation mode for %s!", dev->name);
Przemyslaw Marczak5d387d02015-05-13 13:38:32 +0200164 return -EINVAL;
165}
166
167static int out_set_mode(struct udevice *dev, int mode)
168{
169 struct dm_regulator_uclass_platdata *uc_pdata;
170 int reg_val = -1;
171 uint reg;
172 int ret;
173 int i;
174
175 uc_pdata = dev_get_uclass_platdata(dev);
176
177 if (mode >= uc_pdata->mode_count)
178 return -EINVAL;
179
180 for (i = 0; i < uc_pdata->mode_count; i++) {
181 if (mode == uc_pdata->mode[i].id) {
182 reg_val = uc_pdata->mode[i].register_value;
183 break;
184 }
185 }
186
187 if (reg_val == -1) {
Masahiro Yamada9b643e32017-09-16 14:10:41 +0900188 pr_err("Unknown operation mode for %s!", dev->name);
Przemyslaw Marczak5d387d02015-05-13 13:38:32 +0200189 return -EINVAL;
190 }
191
192 reg = (dev->driver_data - 1) * OUT_REG_COUNT + OUT_REG_OM;
193 ret = pmic_write(dev->parent, reg, (uint8_t *)&reg_val, 1);
194 if (ret) {
Masahiro Yamada9b643e32017-09-16 14:10:41 +0900195 pr_err("PMIC write failed: %d\n", ret);
Przemyslaw Marczak5d387d02015-05-13 13:38:32 +0200196 return ret;
197 }
198
199 return 0;
200}
201
202static int buck_get_voltage(struct udevice *dev)
203{
204 return out_get_value(dev, SANDBOX_BUCK_COUNT, OUT_REG_UV,
205 buck_voltage_range);
206}
207
208static int buck_set_voltage(struct udevice *dev, int uV)
209{
210 return out_set_value(dev, SANDBOX_BUCK_COUNT, OUT_REG_UV,
211 buck_voltage_range, uV);
212}
213
214static int buck_get_current(struct udevice *dev)
215{
216 /* BUCK2 - unsupported */
217 if (dev->driver_data == 2)
218 return -ENOSYS;
219
220 return out_get_value(dev, SANDBOX_BUCK_COUNT, OUT_REG_UA,
221 buck_current_range);
222}
223
224static int buck_set_current(struct udevice *dev, int uA)
225{
226 /* BUCK2 - unsupported */
227 if (dev->driver_data == 2)
228 return -ENOSYS;
229
230 return out_set_value(dev, SANDBOX_BUCK_COUNT, OUT_REG_UA,
231 buck_current_range, uA);
232}
233
Keerthy3bbe7c12017-06-13 09:53:53 +0530234static int buck_get_enable(struct udevice *dev)
Przemyslaw Marczak5d387d02015-05-13 13:38:32 +0200235{
236 if (out_get_mode(dev) == BUCK_OM_OFF)
237 return false;
238
239 return true;
240}
241
242static int buck_set_enable(struct udevice *dev, bool enable)
243{
244 return out_set_mode(dev, enable ? BUCK_OM_ON : BUCK_OM_OFF);
245}
246
247static int sandbox_buck_probe(struct udevice *dev)
248{
249 struct dm_regulator_uclass_platdata *uc_pdata;
250
251 uc_pdata = dev_get_uclass_platdata(dev);
252
253 uc_pdata->type = REGULATOR_TYPE_BUCK;
254 uc_pdata->mode = sandbox_buck_modes;
255 uc_pdata->mode_count = ARRAY_SIZE(sandbox_buck_modes);
256
257 return 0;
258}
259
260static const struct dm_regulator_ops sandbox_buck_ops = {
261 .get_value = buck_get_voltage,
262 .set_value = buck_set_voltage,
263 .get_current = buck_get_current,
264 .set_current = buck_set_current,
265 .get_enable = buck_get_enable,
266 .set_enable = buck_set_enable,
267 .get_mode = out_get_mode,
268 .set_mode = out_set_mode,
269};
270
271U_BOOT_DRIVER(sandbox_buck) = {
272 .name = SANDBOX_BUCK_DRIVER,
273 .id = UCLASS_REGULATOR,
274 .ops = &sandbox_buck_ops,
275 .probe = sandbox_buck_probe,
276};
277
278static int ldo_get_voltage(struct udevice *dev)
279{
280 return out_get_value(dev, SANDBOX_LDO_COUNT, OUT_REG_UV,
281 ldo_voltage_range);
282}
283
284static int ldo_set_voltage(struct udevice *dev, int uV)
285{
286 return out_set_value(dev, SANDBOX_LDO_COUNT, OUT_REG_UV,
287 ldo_voltage_range, uV);
288}
289
290static int ldo_get_current(struct udevice *dev)
291{
292 /* LDO2 - unsupported */
293 if (dev->driver_data == 2)
294 return -ENOSYS;
295
296 return out_get_value(dev, SANDBOX_LDO_COUNT, OUT_REG_UA,
297 ldo_current_range);
298}
299
300static int ldo_set_current(struct udevice *dev, int uA)
301{
302 /* LDO2 - unsupported */
303 if (dev->driver_data == 2)
304 return -ENOSYS;
305
306 return out_set_value(dev, SANDBOX_LDO_COUNT, OUT_REG_UA,
307 ldo_current_range, uA);
308}
309
Keerthy3bbe7c12017-06-13 09:53:53 +0530310static int ldo_get_enable(struct udevice *dev)
Przemyslaw Marczak5d387d02015-05-13 13:38:32 +0200311{
312 if (out_get_mode(dev) == LDO_OM_OFF)
313 return false;
314
315 return true;
316}
317
318static int ldo_set_enable(struct udevice *dev, bool enable)
319{
320 return out_set_mode(dev, enable ? LDO_OM_ON : LDO_OM_OFF);
321}
322
323static int sandbox_ldo_probe(struct udevice *dev)
324{
325 struct dm_regulator_uclass_platdata *uc_pdata;
326
327 uc_pdata = dev_get_uclass_platdata(dev);
328
329 uc_pdata->type = REGULATOR_TYPE_LDO;
330 uc_pdata->mode = sandbox_ldo_modes;
331 uc_pdata->mode_count = ARRAY_SIZE(sandbox_ldo_modes);
332
333 return 0;
334}
335
336static const struct dm_regulator_ops sandbox_ldo_ops = {
337 .get_value = ldo_get_voltage,
338 .set_value = ldo_set_voltage,
339 .get_current = ldo_get_current,
340 .set_current = ldo_set_current,
341 .get_enable = ldo_get_enable,
342 .set_enable = ldo_set_enable,
343 .get_mode = out_get_mode,
344 .set_mode = out_set_mode,
345};
346
347U_BOOT_DRIVER(sandbox_ldo) = {
348 .name = SANDBOX_LDO_DRIVER,
349 .id = UCLASS_REGULATOR,
350 .ops = &sandbox_ldo_ops,
351 .probe = sandbox_ldo_probe,
352};