Skip to content

Commit 89383e0

Browse files
committed
power: battery: MAX17047 fuelgauge support
1 parent 7c132d3 commit 89383e0

File tree

7 files changed

+481
-0
lines changed

7 files changed

+481
-0
lines changed

drivers/power/battery/Kconfig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,8 @@ config DM_BATTERY
44
---help---
55
This config enables driver model battery support.
66

7+
config DM_BATTERY_MAX17047
8+
bool "MAX17047 battery/fuelgauge driver"
9+
depends on DM_BATTERY && DM_PMIC_MAX17047
10+
help
11+
Enable support for the fuelgauge functionality found in MAX17047.

drivers/power/battery/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ obj-$(CONFIG_POWER_BATTERY_TRATS) += bat_trats.o
99
obj-$(CONFIG_POWER_BATTERY_TRATS2) += bat_trats2.o
1010

1111
obj-$(CONFIG_DM_BATTERY) += battery-uclass.o
12+
obj-$(CONFIG_DM_BATTERY_MAX17047) += max17047.o

drivers/power/battery/max17047.c

Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
/*
2+
* Copyright (C) 2018 Simon Shields <simon@lineageos.org>
3+
*
4+
* SPDX-License-Identifier: GPL-2.0+
5+
*/
6+
7+
#include <common.h>
8+
#include <errno.h>
9+
#include <dm.h>
10+
#include <dm/uclass-internal.h>
11+
#include <power/battery.h>
12+
#include <power/pmic.h>
13+
#include <power/max17047.h>
14+
15+
#define CHARACTER_SIZE 16
16+
#define AVG_SAMPLE_COUNT 5
17+
18+
#define MAX17047_EXT_VOLTAGE_THRESH 3850000
19+
#define MAX17047_LOW_VOLTAGE_THRESH 3600000
20+
21+
struct max17047_fg_data {
22+
u16 character0[CHARACTER_SIZE];
23+
u16 character1[CHARACTER_SIZE];
24+
u16 character2[CHARACTER_SIZE];
25+
u16 rcomp0;
26+
u16 tempco;
27+
};
28+
29+
static int max17047_i2c_read(struct udevice *dev, int reg, u16 *data)
30+
{
31+
u8 buf[2] = {0};
32+
33+
int ret = pmic_read(dev->parent, reg, buf, 2);
34+
if (ret < 0)
35+
return ret;
36+
37+
*data = le16_to_cpu(buf[1] << 8 | buf[0]);
38+
39+
return 0;
40+
}
41+
42+
static int max17047_i2c_write(struct udevice *dev, int reg, u16 data)
43+
{
44+
u8 buf[2] = {0};
45+
46+
buf[1] = cpu_to_le16(data) >> 8;
47+
buf[0] = cpu_to_le16(data) & 0xff;
48+
49+
return pmic_write(dev->parent, reg, buf, 2);
50+
}
51+
52+
static int max17047_get_voltage(struct udevice *dev, unsigned int *uV)
53+
{
54+
u16 data;
55+
int ret = max17047_i2c_read(dev, MAX17047_VCELL, &data);
56+
if (ret)
57+
return ret;
58+
*uV = 625 * (data >> 3);
59+
return 0;
60+
}
61+
62+
static u32 max17047_get_average_voltage(struct udevice *dev)
63+
{
64+
u32 vcell_data;
65+
u32 vcell_max = 0;
66+
u32 vcell_min = 0;
67+
u32 vcell_total = 0;
68+
int err;
69+
70+
for (int i = 0; i < AVG_SAMPLE_COUNT; i++) {
71+
err = max17047_get_voltage(dev, &vcell_data);
72+
if (err) {
73+
printf("get_voltage failed: %d\n", err);
74+
return 0;
75+
}
76+
if (i == 0) {
77+
vcell_min = vcell_max = vcell_data;
78+
} else {
79+
if (vcell_data > vcell_max)
80+
vcell_max = vcell_data;
81+
else if (vcell_data < vcell_min)
82+
vcell_min = vcell_data;
83+
}
84+
vcell_total += vcell_data;
85+
}
86+
87+
vcell_total -= vcell_max;
88+
vcell_total -= vcell_min;
89+
90+
return vcell_total / (AVG_SAMPLE_COUNT - 2);
91+
}
92+
93+
static int max17047_get_soc(struct udevice *dev)
94+
{
95+
u16 data;
96+
int err = max17047_i2c_read(dev, MAX17047_VFSOC, &data);
97+
if (err)
98+
return err;
99+
return data >> 8;
100+
}
101+
102+
static int max17047_get_status(struct udevice *dev)
103+
{
104+
u32 voltage;
105+
int soc;
106+
int ret = max17047_get_voltage(dev, &voltage);
107+
if (ret)
108+
return ret;
109+
110+
soc = max17047_get_soc(dev);
111+
if (soc < 0)
112+
return soc;
113+
114+
if (voltage > MAX17047_EXT_VOLTAGE_THRESH)
115+
return BAT_STATE_NOT_PRESENT;
116+
else if (voltage < MAX17047_LOW_VOLTAGE_THRESH || soc < 5)
117+
return BAT_STATE_NEED_CHARGING;
118+
119+
return BAT_STATE_NORMAL;
120+
}
121+
122+
static int max17047_write_array(struct udevice *dev, int reg, u16 *data, int length)
123+
{
124+
int err;
125+
for (int i = 0; i < length; i++) {
126+
err = max17047_i2c_write(dev, reg + i, data[i]);
127+
if (err)
128+
return err;
129+
}
130+
131+
return 0;
132+
}
133+
134+
static int max17047_read_array(struct udevice *dev, int reg, u16 *data, int length)
135+
{
136+
int err;
137+
for (int i = 0; i < length; i++) {
138+
err = max17047_i2c_read(dev, reg + i, &data[i]);
139+
if (err < 0) {
140+
printf("read %#x failed: %d\n", reg + i, err);
141+
return err;
142+
}
143+
}
144+
145+
return 0;
146+
}
147+
148+
static int max17047_write_and_verify(struct udevice *dev, int reg, u16 data)
149+
{
150+
int ret = 0;
151+
ret = max17047_i2c_write(dev, reg, data);
152+
if (ret)
153+
return ret;
154+
ret = max17047_i2c_read(dev, reg, &data);
155+
if (ret)
156+
return ret;
157+
return data;
158+
}
159+
160+
static int max17047_power_on_reset(struct udevice *dev)
161+
{
162+
struct max17047_fg_data *priv = dev->priv;
163+
u16 status;
164+
u16 data0[16];
165+
u16 data1[16];
166+
u16 data2[16];
167+
int write_tries = 5;
168+
bool ok = true;
169+
170+
u32 vcell, soc;
171+
172+
vcell = max17047_get_average_voltage(dev);
173+
soc = max17047_get_soc(dev);
174+
175+
debug("%s: vcell: %u, soc: %u\n", __func__, vcell, soc);
176+
/* delay 500ms */
177+
mdelay(500);
178+
179+
max17047_i2c_write(dev, MAX17047_CONFIG, 0x2310);
180+
181+
do_write:
182+
do {
183+
ok = true;
184+
/* Unlock model */
185+
max17047_i2c_write(dev, MAX17047_MLOCKReg1, 0x59);
186+
max17047_i2c_write(dev, MAX17047_MLOCKReg2, 0xc4);
187+
/* Update model */
188+
max17047_write_array(dev, MAX17047_MODEL1, priv->character0, 16);
189+
max17047_write_array(dev, MAX17047_MODEL2, priv->character1, 16);
190+
max17047_write_array(dev, MAX17047_MODEL3, priv->character2, 16);
191+
/* Check model */
192+
max17047_read_array(dev, MAX17047_MODEL1, data0, 16);
193+
max17047_read_array(dev, MAX17047_MODEL2, data1, 16);
194+
max17047_read_array(dev, MAX17047_MODEL3, data2, 16);
195+
196+
for (int i = 0; i < 16; i++) {
197+
if (priv->character0[i] != data0[i]) {
198+
ok = false;
199+
}
200+
if (priv->character1[i] != data1[i]) {
201+
ok = false;
202+
}
203+
if (priv->character2[i] != data2[i]) {
204+
ok = false;
205+
}
206+
207+
if (!ok)
208+
break;
209+
}
210+
} while (!ok && write_tries-- > 0);
211+
212+
if (!ok) {
213+
printf("%s: Failed to write model!\n", __func__);
214+
return -EIO;
215+
}
216+
217+
ok = true;
218+
/* relock model */
219+
max17047_i2c_write(dev, MAX17047_MLOCKReg1, 0);
220+
max17047_i2c_write(dev, MAX17047_MLOCKReg2, 0);
221+
222+
/* Check model was locked - it should be all zero */
223+
max17047_read_array(dev, MAX17047_MODEL1, data0, 16);
224+
max17047_read_array(dev, MAX17047_MODEL2, data1, 16);
225+
max17047_read_array(dev, MAX17047_MODEL3, data2, 16);
226+
227+
for (int i = 0; i < 16; i++) {
228+
if (data0[i] || data1[i] || data2[i]) {
229+
ok = false;
230+
break;
231+
}
232+
}
233+
234+
if (!ok && write_tries) {
235+
/* model lock failed, try to rewrite it */
236+
printf("%s: model lock failed, attempting to rewrite...\n", __func__);
237+
goto do_write;
238+
} else if (!ok) {
239+
printf("%s: model lock failed, ignoring...\n", __func__);
240+
}
241+
242+
max17047_write_and_verify(dev, MAX17047_RCOMP0, priv->rcomp0);
243+
max17047_write_and_verify(dev, MAX17047_TEMPCO, priv->tempco);
244+
245+
mdelay(350);
246+
max17047_i2c_read(dev, MAX17047_STATUS, &status);
247+
status &= 0xfffd;
248+
max17047_write_and_verify(dev, MAX17047_STATUS, status);
249+
250+
debug("%s: POR completed successfully. SOC: %d\n", __func__, max17047_get_soc(dev));
251+
return 0;
252+
}
253+
254+
255+
static int max17047_probe(struct udevice *dev)
256+
{
257+
int ret;
258+
u16 status;
259+
260+
ret = max17047_i2c_read(dev, MAX17047_STATUS, &status);
261+
if (ret) {
262+
printf("%s: failed to read status register: %d\n", __func__, ret);
263+
return ret;
264+
}
265+
266+
if (status & MAX17047_STATUS_POR) {
267+
ret = max17047_power_on_reset(dev);
268+
if (ret)
269+
return ret;
270+
}
271+
return 0;
272+
}
273+
274+
static int max17047_ofdata_to_platdata(struct udevice *dev)
275+
{
276+
struct max17047_fg_data *priv = dev->priv;
277+
u32 data[CHARACTER_SIZE];
278+
u32 val;
279+
int err, i;
280+
281+
err = dev_read_u32_array(dev, "maxim,cell-character0", data, CHARACTER_SIZE);
282+
if (err < 0) {
283+
printf("Failed to read cell-character0: %d\n", err);
284+
return err;
285+
}
286+
287+
for (i = 0; i < CHARACTER_SIZE; i++) {
288+
priv->character0[i] = data[i] & 0xffff;
289+
}
290+
291+
err = dev_read_u32_array(dev, "maxim,cell-character1", data, CHARACTER_SIZE);
292+
if (err < 0) {
293+
printf("Failed to read cell-character1: %d\n", err);
294+
return err;
295+
}
296+
297+
for (i = 0; i < CHARACTER_SIZE; i++) {
298+
priv->character1[i] = data[i] & 0xffff;
299+
}
300+
301+
err = dev_read_u32_array(dev, "maxim,cell-character2", data, CHARACTER_SIZE);
302+
if (err < 0) {
303+
printf("Failed to read cell-character2: %d\n", err);
304+
return err;
305+
}
306+
307+
for (i = 0; i < CHARACTER_SIZE; i++) {
308+
priv->character2[i] = data[i] & 0xffff;
309+
}
310+
311+
err = dev_read_u32(dev, "maxim,rcomp0", &val);
312+
if (err < 0) {
313+
printf("Failed to read rcomp0: %d\n", err);
314+
return err;
315+
}
316+
317+
priv->rcomp0 = val & 0xffff;
318+
319+
err = dev_read_u32(dev, "maxim,tempco", &val);
320+
if (err < 0) {
321+
printf("Failed to read tempco: %d\n", err);
322+
return err;
323+
}
324+
325+
priv->tempco = val & 0xffff;
326+
327+
return 0;
328+
}
329+
330+
static struct dm_battery_ops max17047_battery_ops = {
331+
.get_voltage = max17047_get_voltage,
332+
.get_status = max17047_get_status,
333+
.get_soc = max17047_get_soc,
334+
};
335+
336+
U_BOOT_DRIVER(battery_max17047) = {
337+
.name = MAX17047_FUELGAUGE_DRIVER,
338+
.id = UCLASS_BATTERY,
339+
.ops = &max17047_battery_ops,
340+
.probe = max17047_probe,
341+
.ofdata_to_platdata = max17047_ofdata_to_platdata,
342+
.priv_auto_alloc_size = sizeof(struct max17047_fg_data),
343+
};
344+

drivers/power/pmic/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ config DM_PMIC_PFUZE100
5555
This config enables implementation of driver-model pmic uclass features
5656
for PMIC PFUZE100. The driver implements read/write operations.
5757

58+
config DM_PMIC_MAX17047
59+
bool "Enable Driver Model for PMIC MAX17047"
60+
depends on DM_PMIC
61+
---help---
62+
This config enables implementation of driver-model pmic uclass features
63+
for MAX17047. This driver implements read/write operations
64+
5865
config DM_PMIC_MAX77686
5966
bool "Enable Driver Model for PMIC MAX77686"
6067
depends on DM_PMIC

drivers/power/pmic/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#
77

88
obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
9+
obj-$(CONFIG_DM_PMIC_MAX17047) += max17047.o
910
obj-$(CONFIG_DM_PMIC_MAX77686) += max77686.o
1011
obj-$(CONFIG_DM_PMIC_MAX77693) += max77693.o
1112
obj-$(CONFIG_DM_PMIC_MAX8998) += max8998.o

0 commit comments

Comments
 (0)