blob: d96a1f85e8fa1ece109da162aeb41dbde1d78657 [file] [log] [blame]
Simon Glassadecfef2016-01-21 19:43:30 -07001/*
2 * Copyright (C) 2015 Google, Inc
3 * Written by Simon Glass <sjg@chromium.org>
4 *
5 * Based on Rockchip's drivers/power/pmic/pmic_rk808.c:
6 * Copyright (C) 2012 rockchips
7 * zyw <zyw@rock-chips.com>
8 *
9 * SPDX-License-Identifier: GPL-2.0+
10 */
11
12#include <common.h>
13#include <dm.h>
14#include <errno.h>
Jacob Chen614704b2017-05-02 14:54:52 +080015#include <power/rk8xx_pmic.h>
Simon Glassadecfef2016-01-21 19:43:30 -070016#include <power/pmic.h>
17#include <power/regulator.h>
18
19#ifndef CONFIG_SPL_BUILD
20#define ENABLE_DRIVER
21#endif
22
Jacob Chen21a6a252017-05-02 14:54:50 +080023/* Field Definitions */
24#define RK808_BUCK_VSEL_MASK 0x3f
25#define RK808_BUCK4_VSEL_MASK 0xf
26#define RK808_LDO_VSEL_MASK 0x1f
27
Jacob Chen36163e32017-05-02 14:54:51 +080028#define RK818_BUCK_VSEL_MASK 0x3f
29#define RK818_BUCK4_VSEL_MASK 0x1f
30#define RK818_LDO_VSEL_MASK 0x1f
31#define RK818_LDO3_ON_VSEL_MASK 0xf
32#define RK818_BOOST_ON_VSEL_MASK 0xe0
33
Jacob Chen614704b2017-05-02 14:54:52 +080034struct rk8xx_reg_info {
Simon Glassadecfef2016-01-21 19:43:30 -070035 uint min_uv;
36 uint step_uv;
37 s8 vsel_reg;
Jacob Chen21a6a252017-05-02 14:54:50 +080038 u8 vsel_mask;
Simon Glassadecfef2016-01-21 19:43:30 -070039};
40
Jacob Chen614704b2017-05-02 14:54:52 +080041static const struct rk8xx_reg_info rk808_buck[] = {
Jacob Chen21a6a252017-05-02 14:54:50 +080042 { 712500, 12500, REG_BUCK1_ON_VSEL, RK808_BUCK_VSEL_MASK, },
43 { 712500, 12500, REG_BUCK2_ON_VSEL, RK808_BUCK_VSEL_MASK, },
44 { 712500, 12500, -1, RK808_BUCK_VSEL_MASK, },
45 { 1800000, 100000, REG_BUCK4_ON_VSEL, RK808_BUCK4_VSEL_MASK, },
Simon Glassadecfef2016-01-21 19:43:30 -070046};
47
Wadim Egorov4c4905f2017-06-19 12:36:38 +020048static const struct rk8xx_reg_info rk818_buck[] = {
49 { 712500, 12500, REG_BUCK1_ON_VSEL, RK818_BUCK_VSEL_MASK, },
50 { 712500, 12500, REG_BUCK2_ON_VSEL, RK818_BUCK_VSEL_MASK, },
51 { 712500, 12500, -1, RK818_BUCK_VSEL_MASK, },
52 { 1800000, 100000, REG_BUCK4_ON_VSEL, RK818_BUCK4_VSEL_MASK, },
53};
54
55#ifdef ENABLE_DRIVER
Jacob Chen614704b2017-05-02 14:54:52 +080056static const struct rk8xx_reg_info rk808_ldo[] = {
Jacob Chen21a6a252017-05-02 14:54:50 +080057 { 1800000, 100000, REG_LDO1_ON_VSEL, RK808_LDO_VSEL_MASK, },
58 { 1800000, 100000, REG_LDO2_ON_VSEL, RK808_LDO_VSEL_MASK, },
59 { 800000, 100000, REG_LDO3_ON_VSEL, RK808_BUCK4_VSEL_MASK, },
60 { 1800000, 100000, REG_LDO4_ON_VSEL, RK808_LDO_VSEL_MASK, },
61 { 1800000, 100000, REG_LDO5_ON_VSEL, RK808_LDO_VSEL_MASK, },
62 { 800000, 100000, REG_LDO6_ON_VSEL, RK808_LDO_VSEL_MASK, },
63 { 800000, 100000, REG_LDO7_ON_VSEL, RK808_LDO_VSEL_MASK, },
64 { 1800000, 100000, REG_LDO8_ON_VSEL, RK808_LDO_VSEL_MASK, },
Simon Glassadecfef2016-01-21 19:43:30 -070065};
66
Jacob Chen614704b2017-05-02 14:54:52 +080067static const struct rk8xx_reg_info rk818_ldo[] = {
Jacob Chen36163e32017-05-02 14:54:51 +080068 { 1800000, 100000, REG_LDO1_ON_VSEL, RK818_LDO_VSEL_MASK, },
69 { 1800000, 100000, REG_LDO2_ON_VSEL, RK818_LDO_VSEL_MASK, },
70 { 800000, 100000, REG_LDO3_ON_VSEL, RK818_LDO3_ON_VSEL_MASK, },
71 { 1800000, 100000, REG_LDO4_ON_VSEL, RK818_LDO_VSEL_MASK, },
72 { 1800000, 100000, REG_LDO5_ON_VSEL, RK818_LDO_VSEL_MASK, },
73 { 800000, 100000, REG_LDO6_ON_VSEL, RK818_LDO_VSEL_MASK, },
74 { 800000, 100000, REG_LDO7_ON_VSEL, RK818_LDO_VSEL_MASK, },
75 { 1800000, 100000, REG_LDO8_ON_VSEL, RK818_LDO_VSEL_MASK, },
76};
Wadim Egorov4c4905f2017-06-19 12:36:38 +020077#endif
Jacob Chen36163e32017-05-02 14:54:51 +080078
Jacob Chen614704b2017-05-02 14:54:52 +080079static const struct rk8xx_reg_info *get_buck_reg(struct udevice *pmic,
Jacob Chen36163e32017-05-02 14:54:51 +080080 int num)
81{
Jacob Chen614704b2017-05-02 14:54:52 +080082 struct rk8xx_priv *priv = dev_get_priv(pmic);
83 switch (priv->variant) {
Jacob Chen36163e32017-05-02 14:54:51 +080084 case RK818_ID:
85 return &rk818_buck[num];
86 default:
87 return &rk808_buck[num];
88 }
89}
90
Simon Glassadecfef2016-01-21 19:43:30 -070091static int _buck_set_value(struct udevice *pmic, int buck, int uvolt)
92{
Jacob Chen614704b2017-05-02 14:54:52 +080093 const struct rk8xx_reg_info *info = get_buck_reg(pmic, buck - 1);
Jacob Chen21a6a252017-05-02 14:54:50 +080094 int mask = info->vsel_mask;
Simon Glassadecfef2016-01-21 19:43:30 -070095 int val;
96
97 if (info->vsel_reg == -1)
98 return -ENOSYS;
99 val = (uvolt - info->min_uv) / info->step_uv;
100 debug("%s: reg=%x, mask=%x, val=%x\n", __func__, info->vsel_reg, mask,
101 val);
102
103 return pmic_clrsetbits(pmic, info->vsel_reg, mask, val);
104}
105
106static int _buck_set_enable(struct udevice *pmic, int buck, bool enable)
107{
108 uint mask;
109 int ret;
110
111 buck--;
112 mask = 1 << buck;
113 if (enable) {
Jacob Chen0237cfd2017-05-02 14:54:48 +0800114 ret = pmic_clrsetbits(pmic, REG_DCDC_ILMAX, 0, 3 << (buck * 2));
Simon Glassadecfef2016-01-21 19:43:30 -0700115 if (ret)
116 return ret;
117 ret = pmic_clrsetbits(pmic, REG_DCDC_UV_ACT, 1 << buck, 0);
118 if (ret)
119 return ret;
120 }
121
122 return pmic_clrsetbits(pmic, REG_DCDC_EN, mask, enable ? mask : 0);
123}
124
125#ifdef ENABLE_DRIVER
Wadim Egorov4c4905f2017-06-19 12:36:38 +0200126static const struct rk8xx_reg_info *get_ldo_reg(struct udevice *pmic,
127 int num)
128{
129 struct rk8xx_priv *priv = dev_get_priv(pmic);
130 switch (priv->variant) {
131 case RK818_ID:
132 return &rk818_ldo[num];
133 default:
134 return &rk808_ldo[num];
135 }
136}
137
Simon Glassadecfef2016-01-21 19:43:30 -0700138static int buck_get_value(struct udevice *dev)
139{
140 int buck = dev->driver_data - 1;
Jacob Chen614704b2017-05-02 14:54:52 +0800141 const struct rk8xx_reg_info *info = get_buck_reg(dev->parent, buck);
Jacob Chen21a6a252017-05-02 14:54:50 +0800142 int mask = info->vsel_mask;
Simon Glassadecfef2016-01-21 19:43:30 -0700143 int ret, val;
144
145 if (info->vsel_reg == -1)
146 return -ENOSYS;
147 ret = pmic_reg_read(dev->parent, info->vsel_reg);
148 if (ret < 0)
149 return ret;
150 val = ret & mask;
151
152 return info->min_uv + val * info->step_uv;
153}
154
155static int buck_set_value(struct udevice *dev, int uvolt)
156{
157 int buck = dev->driver_data;
158
159 return _buck_set_value(dev->parent, buck, uvolt);
160}
161
162static int buck_set_enable(struct udevice *dev, bool enable)
163{
164 int buck = dev->driver_data;
165
166 return _buck_set_enable(dev->parent, buck, enable);
167}
168
169static bool buck_get_enable(struct udevice *dev)
170{
171 int buck = dev->driver_data - 1;
172 int ret;
173 uint mask;
174
175 mask = 1 << buck;
176
177 ret = pmic_reg_read(dev->parent, REG_DCDC_EN);
178 if (ret < 0)
179 return ret;
180
181 return ret & mask ? true : false;
182}
183
184static int ldo_get_value(struct udevice *dev)
185{
186 int ldo = dev->driver_data - 1;
Jacob Chen614704b2017-05-02 14:54:52 +0800187 const struct rk8xx_reg_info *info = get_ldo_reg(dev->parent, ldo);
Jacob Chen21a6a252017-05-02 14:54:50 +0800188 int mask = info->vsel_mask;
Simon Glassadecfef2016-01-21 19:43:30 -0700189 int ret, val;
190
191 if (info->vsel_reg == -1)
192 return -ENOSYS;
193 ret = pmic_reg_read(dev->parent, info->vsel_reg);
194 if (ret < 0)
195 return ret;
196 val = ret & mask;
197
198 return info->min_uv + val * info->step_uv;
199}
200
201static int ldo_set_value(struct udevice *dev, int uvolt)
202{
203 int ldo = dev->driver_data - 1;
Jacob Chen614704b2017-05-02 14:54:52 +0800204 const struct rk8xx_reg_info *info = get_ldo_reg(dev->parent, ldo);
Jacob Chen21a6a252017-05-02 14:54:50 +0800205 int mask = info->vsel_mask;
Simon Glassadecfef2016-01-21 19:43:30 -0700206 int val;
207
208 if (info->vsel_reg == -1)
209 return -ENOSYS;
210 val = (uvolt - info->min_uv) / info->step_uv;
211 debug("%s: reg=%x, mask=%x, val=%x\n", __func__, info->vsel_reg, mask,
212 val);
213
214 return pmic_clrsetbits(dev->parent, info->vsel_reg, mask, val);
215}
216
217static int ldo_set_enable(struct udevice *dev, bool enable)
218{
219 int ldo = dev->driver_data - 1;
220 uint mask;
221
222 mask = 1 << ldo;
223
224 return pmic_clrsetbits(dev->parent, REG_LDO_EN, mask,
225 enable ? mask : 0);
226}
227
228static bool ldo_get_enable(struct udevice *dev)
229{
230 int ldo = dev->driver_data - 1;
231 int ret;
232 uint mask;
233
234 mask = 1 << ldo;
235
236 ret = pmic_reg_read(dev->parent, REG_LDO_EN);
237 if (ret < 0)
238 return ret;
239
240 return ret & mask ? true : false;
241}
242
243static int switch_set_enable(struct udevice *dev, bool enable)
244{
245 int sw = dev->driver_data - 1;
246 uint mask;
247
248 mask = 1 << (sw + 5);
249
250 return pmic_clrsetbits(dev->parent, REG_DCDC_EN, mask,
251 enable ? mask : 0);
252}
253
254static bool switch_get_enable(struct udevice *dev)
255{
256 int sw = dev->driver_data - 1;
257 int ret;
258 uint mask;
259
260 mask = 1 << (sw + 5);
261
262 ret = pmic_reg_read(dev->parent, REG_DCDC_EN);
263 if (ret < 0)
264 return ret;
265
266 return ret & mask ? true : false;
267}
268
Jacob Chen614704b2017-05-02 14:54:52 +0800269static int rk8xx_buck_probe(struct udevice *dev)
Simon Glassadecfef2016-01-21 19:43:30 -0700270{
271 struct dm_regulator_uclass_platdata *uc_pdata;
272
273 uc_pdata = dev_get_uclass_platdata(dev);
274
275 uc_pdata->type = REGULATOR_TYPE_BUCK;
276 uc_pdata->mode_count = 0;
277
278 return 0;
279}
280
Jacob Chen614704b2017-05-02 14:54:52 +0800281static int rk8xx_ldo_probe(struct udevice *dev)
Simon Glassadecfef2016-01-21 19:43:30 -0700282{
283 struct dm_regulator_uclass_platdata *uc_pdata;
284
285 uc_pdata = dev_get_uclass_platdata(dev);
286
287 uc_pdata->type = REGULATOR_TYPE_LDO;
288 uc_pdata->mode_count = 0;
289
290 return 0;
291}
292
Jacob Chen614704b2017-05-02 14:54:52 +0800293static int rk8xx_switch_probe(struct udevice *dev)
Simon Glassadecfef2016-01-21 19:43:30 -0700294{
295 struct dm_regulator_uclass_platdata *uc_pdata;
296
297 uc_pdata = dev_get_uclass_platdata(dev);
298
299 uc_pdata->type = REGULATOR_TYPE_FIXED;
300 uc_pdata->mode_count = 0;
301
302 return 0;
303}
304
Jacob Chen614704b2017-05-02 14:54:52 +0800305static const struct dm_regulator_ops rk8xx_buck_ops = {
Simon Glassadecfef2016-01-21 19:43:30 -0700306 .get_value = buck_get_value,
307 .set_value = buck_set_value,
308 .get_enable = buck_get_enable,
309 .set_enable = buck_set_enable,
310};
311
Jacob Chen614704b2017-05-02 14:54:52 +0800312static const struct dm_regulator_ops rk8xx_ldo_ops = {
Simon Glassadecfef2016-01-21 19:43:30 -0700313 .get_value = ldo_get_value,
314 .set_value = ldo_set_value,
315 .get_enable = ldo_get_enable,
316 .set_enable = ldo_set_enable,
317};
318
Jacob Chen614704b2017-05-02 14:54:52 +0800319static const struct dm_regulator_ops rk8xx_switch_ops = {
Simon Glassadecfef2016-01-21 19:43:30 -0700320 .get_enable = switch_get_enable,
321 .set_enable = switch_set_enable,
322};
323
Jacob Chen614704b2017-05-02 14:54:52 +0800324U_BOOT_DRIVER(rk8xx_buck) = {
325 .name = "rk8xx_buck",
Simon Glassadecfef2016-01-21 19:43:30 -0700326 .id = UCLASS_REGULATOR,
Jacob Chen614704b2017-05-02 14:54:52 +0800327 .ops = &rk8xx_buck_ops,
328 .probe = rk8xx_buck_probe,
Simon Glassadecfef2016-01-21 19:43:30 -0700329};
330
Jacob Chen614704b2017-05-02 14:54:52 +0800331U_BOOT_DRIVER(rk8xx_ldo) = {
332 .name = "rk8xx_ldo",
Simon Glassadecfef2016-01-21 19:43:30 -0700333 .id = UCLASS_REGULATOR,
Jacob Chen614704b2017-05-02 14:54:52 +0800334 .ops = &rk8xx_ldo_ops,
335 .probe = rk8xx_ldo_probe,
Simon Glassadecfef2016-01-21 19:43:30 -0700336};
337
Jacob Chen614704b2017-05-02 14:54:52 +0800338U_BOOT_DRIVER(rk8xx_switch) = {
339 .name = "rk8xx_switch",
Simon Glassadecfef2016-01-21 19:43:30 -0700340 .id = UCLASS_REGULATOR,
Jacob Chen614704b2017-05-02 14:54:52 +0800341 .ops = &rk8xx_switch_ops,
342 .probe = rk8xx_switch_probe,
Simon Glassadecfef2016-01-21 19:43:30 -0700343};
344#endif
345
Jacob Chen614704b2017-05-02 14:54:52 +0800346int rk8xx_spl_configure_buck(struct udevice *pmic, int buck, int uvolt)
Simon Glassadecfef2016-01-21 19:43:30 -0700347{
348 int ret;
349
350 ret = _buck_set_value(pmic, buck, uvolt);
351 if (ret)
352 return ret;
353
354 return _buck_set_enable(pmic, buck, true);
355}