1 /*
2 * (C) Copyright 2008-2015 Fuzhou Rockchip Electronics Co., Ltd
3 *
4 * SPDX-License-Identifier: GPL-2.0+
5 */
6 #include <common.h>
7 #include <asm/gpio.h>
8 #include <dm.h>
9 #include <dm/device.h>
10 #include <errno.h>
11 #include <fdtdec.h>
12 #include <i2c.h>
13 #include <linux/usb/phy-rockchip-usb2.h>
14 #include <malloc.h>
15 #include <power/battery.h>
16 #include <power/fuel_gauge.h>
17 #include <power/pmic.h>
18 #include "fg_regs.h"
19
20 DECLARE_GLOBAL_DATA_PTR;
21
22 #define COMPAT_ROCKCHIP_CW201X "cw201x"
23
24 #define REG_VERSION 0x0
25 #define REG_VCELL 0x2
26 #define REG_SOC 0x4
27 #define REG_RRT_ALERT 0x6
28 #define REG_CONFIG 0x8
29 #define REG_MODE 0xA
30 #define REG_BATINFO 0x10
31
32 #define MODE_SLEEP_MASK (0x3 << 6)
33 #define MODE_SLEEP (0x3 << 6)
34 #define MODE_NORMAL (0x0 << 6)
35 #define MODE_QUICK_START (0x3 << 4)
36 #define MODE_RESTART (0xf << 0)
37
38 #define CONFIG_UPDATE_FLG (0x1 << 1)
39 #define ATHD (0x0 << 3)
40
41 enum charger_type {
42 CHARGER_TYPE_NO = 0,
43 CHARGER_TYPE_USB,
44 CHARGER_TYPE_AC,
45 CHARGER_TYPE_DC,
46 CHARGER_TYPE_UNDEF,
47 };
48
49 struct cw201x_info {
50 struct udevice *dev;
51 int capacity;
52 u32 *cw_bat_config_info;
53 int divider_res1;
54 int divider_res2;
55 int hw_id_check;
56 struct gpio_desc hw_id0;
57 struct gpio_desc hw_id1;
58 int support_dc_adp;
59 struct gpio_desc dc_det_gpio;
60 int dc_det_flag;
61 bool dual_cell;
62 };
63
cw201x_read(struct cw201x_info * cw201x,u8 reg)64 static u8 cw201x_read(struct cw201x_info *cw201x, u8 reg)
65 {
66 u8 val;
67 int ret;
68
69 ret = dm_i2c_read(cw201x->dev, reg, &val, 1);
70 if (ret) {
71 debug("write error to device: %p register: %#x!",
72 cw201x->dev, reg);
73 return ret;
74 }
75
76 return val;
77 }
78
cw201x_write(struct cw201x_info * cw201x,u8 reg,u8 val)79 static int cw201x_write(struct cw201x_info *cw201x, u8 reg, u8 val)
80 {
81 int ret;
82
83 ret = dm_i2c_write(cw201x->dev, reg, &val, 1);
84 if (ret) {
85 debug("write error to device: %p register: %#x!",
86 cw201x->dev, reg);
87 return ret;
88 }
89
90 return 0;
91 }
92
cw201x_read_half_word(struct cw201x_info * cw201x,int reg)93 static u16 cw201x_read_half_word(struct cw201x_info *cw201x, int reg)
94 {
95 u8 vall, valh;
96 u16 val;
97
98 valh = cw201x_read(cw201x, reg);
99 vall = cw201x_read(cw201x, reg + 1);
100 val = ((u16)valh << 8) | vall;
101
102 return val;
103 }
104
cw201x_parse_config_info(struct cw201x_info * cw201x)105 static int cw201x_parse_config_info(struct cw201x_info *cw201x)
106 {
107 int ret;
108 int i, len, size;
109 const u8 *info;
110 struct udevice *dev = cw201x->dev;
111
112 if (dev_read_prop(dev, "bat_config_info", &len)) {
113 len /= sizeof(u32);
114 size = sizeof(*cw201x->cw_bat_config_info) * len;
115 cw201x->cw_bat_config_info = calloc(size, 1);
116 if (!cw201x->cw_bat_config_info) {
117 debug("calloc cw_bat_config_info fail\n");
118 return -EINVAL;
119 }
120 ret = dev_read_u32_array(dev, "bat_config_info",
121 cw201x->cw_bat_config_info, len);
122 if (ret) {
123 debug("fdtdec_get cw_bat_config_info fail\n");
124 return -EINVAL;
125 }
126
127 return 0;
128 }
129
130 if (!dev_read_prop(dev, "cellwise,battery-profile", &len))
131 return -EINVAL;
132
133 size = sizeof(*cw201x->cw_bat_config_info) * len;
134 cw201x->cw_bat_config_info = calloc(size, 1);
135 if (!cw201x->cw_bat_config_info) {
136 debug("calloc cw_bat_config_info fail\n");
137 return -EINVAL;
138 }
139
140 info = dev_read_u8_array_ptr(dev, "cellwise,battery-profile", len);
141 if (!info) {
142 debug("fdtdec_get battery profile fail\n");
143 return -EINVAL;
144 }
145 for (i = 0; i < len; i++) {
146 cw201x->cw_bat_config_info[i] = info[i];
147 debug("%#x ", cw201x->cw_bat_config_info[i]);
148 if ((i+1) % 8 == 0)
149 debug("\n");
150 }
151
152 return 0;
153 }
154
cw201x_ofdata_to_platdata(struct udevice * dev)155 static int cw201x_ofdata_to_platdata(struct udevice *dev)
156 {
157 struct cw201x_info *cw201x = dev_get_priv(dev);
158 int ret;
159 int hw_id0_val, hw_id1_val;
160
161 cw201x->dev = dev;
162 ret = cw201x_parse_config_info(cw201x);
163 if (ret)
164 return ret;
165
166 cw201x->dual_cell = dev_read_bool(dev, "cellwise,dual-cell");
167 ret = gpio_request_by_name_nodev(dev_ofnode(dev), "dc_det_gpio",
168 0, &cw201x->dc_det_gpio, GPIOD_IS_IN);
169 if (!ret) {
170 cw201x->support_dc_adp = 1;
171 debug("DC is valid\n");
172 } else {
173 debug("DC is invalid, ret=%d\n", ret);
174 }
175
176 cw201x->hw_id_check = dev_read_u32_default(dev, "hw_id_check", 0);
177 if (cw201x->hw_id_check) {
178 ret = gpio_request_by_name_nodev(dev_ofnode(dev),
179 "hw_id0_gpio", 0,
180 &cw201x->hw_id0, GPIOD_IS_IN);
181 if (ret)
182 return -EINVAL;
183 hw_id0_val = dm_gpio_get_value(&cw201x->hw_id0);
184
185 ret = gpio_request_by_name_nodev(dev_ofnode(dev),
186 "hw_id1_gpio", 0,
187 &cw201x->hw_id1, GPIOD_IS_IN);
188 if (ret)
189 return -EINVAL;
190 hw_id1_val = dm_gpio_get_value(&cw201x->hw_id1);
191
192 /* ID1 = 0, ID0 = 1 : Battery */
193 if (!hw_id0_val || hw_id1_val)
194 return -EINVAL;
195 }
196
197 cw201x->divider_res1 = dev_read_u32_default(dev, "divider_res1", 0);
198 cw201x->divider_res2 = dev_read_u32_default(dev, "divider_res2", 0);
199
200 return 0;
201 }
202
cw201x_get_vol(struct cw201x_info * cw201x)203 static int cw201x_get_vol(struct cw201x_info *cw201x)
204 {
205 u16 value16, value16_1, value16_2, value16_3;
206 int voltage;
207 int res1, res2;
208 int retry = 0;
209
210 __retry:
211 value16 = cw201x_read_half_word(cw201x, REG_VCELL);
212 if (value16 < 0)
213 return -1;
214
215 value16_1 = cw201x_read_half_word(cw201x, REG_VCELL);
216 if (value16_1 < 0)
217 return -1;
218
219 value16_2 = cw201x_read_half_word(cw201x, REG_VCELL);
220 if (value16_2 < 0)
221 return -1;
222
223 if (value16 > value16_1) {
224 value16_3 = value16;
225 value16 = value16_1;
226 value16_1 = value16_3;
227 }
228
229 if (value16_1 > value16_2) {
230 value16_3 = value16_1;
231 value16_1 = value16_2;
232 value16_2 = value16_3;
233 }
234
235 if (value16 > value16_1) {
236 value16_3 = value16;
237 value16 = value16_1;
238 value16_1 = value16_3;
239 }
240
241 voltage = value16_1 * 312 / 1024;
242 if (voltage <= 0 && retry < 10) {
243 retry++;
244 mdelay(20);
245 goto __retry;
246 }
247
248 if (cw201x->divider_res1 &&
249 cw201x->divider_res2) {
250 res1 = cw201x->divider_res1;
251 res2 = cw201x->divider_res2;
252 voltage = voltage * (res1 + res2) / res2;
253 }
254
255 if (cw201x->dual_cell)
256 voltage *= 2;
257
258 debug("the cw201x voltage=%d\n", voltage);
259 return voltage;
260 }
261
cw201x_dwc_otg_check_dpdm(void)262 static int cw201x_dwc_otg_check_dpdm(void)
263 {
264 #if defined(CONFIG_PHY_ROCKCHIP_INNO_USB2) && !defined(CONFIG_SPL_BUILD)
265 return rockchip_chg_get_type();
266 #else
267 debug("rockchip_chg_get_type() is not implement\n");
268 return CHARGER_TYPE_NO;
269 #endif
270 }
271
cw201x_get_usb_state(struct cw201x_info * cw201x)272 static int cw201x_get_usb_state(struct cw201x_info *cw201x)
273 {
274 int charger_type;
275
276 switch (cw201x_dwc_otg_check_dpdm()) {
277 case 0:
278 charger_type = CHARGER_TYPE_NO;
279 break;
280 case 1:
281 case 3:
282 charger_type = CHARGER_TYPE_USB;
283 break;
284 case 2:
285 charger_type = CHARGER_TYPE_AC;
286 break;
287 default:
288 charger_type = CHARGER_TYPE_NO;
289 break;
290 }
291
292 return charger_type;
293 }
294
cw201x_get_dc_state(struct cw201x_info * cw201x)295 static bool cw201x_get_dc_state(struct cw201x_info *cw201x)
296 {
297 if (dm_gpio_get_value(&cw201x->dc_det_gpio))
298 return true;
299
300 return false;
301 }
302
cw201x_check_charge(struct cw201x_info * cw201x)303 static bool cw201x_check_charge(struct cw201x_info *cw201x)
304 {
305 if (cw201x_get_usb_state(cw201x) != CHARGER_TYPE_NO)
306 return true;
307 if (cw201x_get_dc_state(cw201x))
308 return true;
309
310 return false;
311 }
312
cw201x_get_soc(struct cw201x_info * cw201x)313 static int cw201x_get_soc(struct cw201x_info *cw201x)
314 {
315 int cap, i = 0;
316
317 while (i < 10) {
318 cap = cw201x_read(cw201x, REG_SOC);
319 if ((cap < 0) || (cap > 100))
320 cap = cw201x->capacity;
321 i++;
322 if (cap)
323 break;
324 }
325 cw201x->capacity = cap;
326
327 return cw201x->capacity;
328 }
329
cw201x_update_get_soc(struct udevice * dev)330 static int cw201x_update_get_soc(struct udevice *dev)
331 {
332 struct cw201x_info *cw201x = dev_get_priv(dev);
333
334 return cw201x_get_soc(cw201x);
335 }
336
cw201x_update_get_voltage(struct udevice * dev)337 static int cw201x_update_get_voltage(struct udevice *dev)
338 {
339 struct cw201x_info *cw201x = dev_get_priv(dev);
340
341 return cw201x_get_vol(cw201x);
342 }
343
cw201x_update_get_current(struct udevice * dev)344 static int cw201x_update_get_current(struct udevice *dev)
345 {
346 return 0;
347 }
348
cw201x_update_get_chrg_online(struct udevice * dev)349 static bool cw201x_update_get_chrg_online(struct udevice *dev)
350 {
351 struct cw201x_info *cw201x = dev_get_priv(dev);
352
353 return cw201x_check_charge(cw201x);
354 }
355
cw201x_capability(struct udevice * dev)356 static int cw201x_capability(struct udevice *dev)
357 {
358 return FG_CAP_FUEL_GAUGE;
359 }
360
361 static struct dm_fuel_gauge_ops cw201x_fg_ops = {
362 .capability = cw201x_capability,
363 .get_soc = cw201x_update_get_soc,
364 .get_voltage = cw201x_update_get_voltage,
365 .get_current = cw201x_update_get_current,
366 .get_chrg_online = cw201x_update_get_chrg_online,
367 };
368
cw201x_fg_cfg(struct cw201x_info * cw201x)369 static int cw201x_fg_cfg(struct cw201x_info *cw201x)
370 {
371 u8 val = MODE_SLEEP;
372 int i;
373
374 if ((val & MODE_SLEEP_MASK) == MODE_SLEEP) {
375 val = MODE_NORMAL;
376 cw201x_write(cw201x, REG_MODE, val);
377 }
378
379 for (i = 0; i < 64; i++) {
380 cw201x_write(cw201x, REG_BATINFO + i,
381 (u8)cw201x->cw_bat_config_info[i]);
382 }
383
384 return 0;
385 }
386
cw201x_fg_probe(struct udevice * dev)387 static int cw201x_fg_probe(struct udevice *dev)
388 {
389 struct cw201x_info *cw201x = dev_get_priv(dev);
390
391 cw201x->dev = dev;
392 cw201x_fg_cfg(cw201x);
393
394 debug("vol: %d, soc: %d\n",
395 cw201x_get_vol(cw201x), cw201x_get_soc(cw201x));
396
397 return 0;
398 }
399
400 static const struct udevice_id cw201x_ids[] = {
401 { .compatible = "cw201x" },
402 { .compatible = "cellwise,cw2015" },
403 { }
404 };
405
406 U_BOOT_DRIVER(cw201x_fg) = {
407 .name = "cw201x_fg",
408 .id = UCLASS_FG,
409 .of_match = cw201x_ids,
410 .probe = cw201x_fg_probe,
411 .ofdata_to_platdata = cw201x_ofdata_to_platdata,
412 .ops = &cw201x_fg_ops,
413 .priv_auto_alloc_size = sizeof(struct cw201x_info),
414 };
415