Skip to content

Commit b16e741

Browse files
committed
drivers: fuelgauge: Add Onsemi LC709203F driver
Add driver for the Onsemi LC709203F fuel gauge Signed-off-by: Philipp Steiner <[email protected]>
1 parent 25c3c13 commit b16e741

File tree

8 files changed

+1049
-0
lines changed

8 files changed

+1049
-0
lines changed

drivers/fuel_gauge/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ add_subdirectory_ifdef(CONFIG_SBS_GAUGE_NEW_API sbs_gauge)
66
add_subdirectory_ifdef(CONFIG_FUEL_GAUGE_COMPOSITE composite)
77
add_subdirectory_ifdef(CONFIG_MAX17048 max17048)
88
add_subdirectory_ifdef(CONFIG_BQ27Z746 bq27z746)
9+
add_subdirectory_ifdef(CONFIG_LC709203F lc709203f)
910

1011
zephyr_library_sources_ifdef(CONFIG_USERSPACE fuel_gauge_syscall_handlers.c)
1112

drivers/fuel_gauge/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@ source "drivers/fuel_gauge/max17048/Kconfig"
2323
source "drivers/fuel_gauge/sbs_gauge/Kconfig"
2424
source "drivers/fuel_gauge/bq27z746/Kconfig"
2525
source "drivers/fuel_gauge/composite/Kconfig"
26+
source "drivers/fuel_gauge/lc709203f/Kconfig"
2627

2728
endif # FUEL_GAUGE
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
zephyr_library()
2+
3+
zephyr_library_sources(lc709203f.c)
4+
5+
zephyr_include_directories_ifdef(CONFIG_EMUL_LC709203F .)
6+
zephyr_library_sources_ifdef(CONFIG_EMUL_LC709203F ./emul_lc709203f.c)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Copyright (c) 2025 Philipp Steiner <[email protected]>
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
config LC709203F
6+
bool "LC709203F Fuel Gauge"
7+
default y
8+
depends on DT_HAS_ONNN_LC709203F_ENABLED
9+
select I2C
10+
help
11+
Enable I2C-based driver for LC709203F Fuel Gauge.
12+
13+
config EMUL_LC709203F
14+
bool "Emulate an LC709203F fuel gague"
15+
default y
16+
depends on EMUL
17+
depends on LC709203F
18+
help
19+
It provides readings which follow a simple sequence, thus allowing
20+
test code to check that things are working as expected.
Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
/*
2+
* Copyright (c) 2025 Philipp Steiner <[email protected]>
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
* Emulator for lc709203f fuel gauge
7+
*/
8+
9+
#include <string.h>
10+
#define DT_DRV_COMPAT onnn_lc709203f
11+
12+
#include <zephyr/logging/log.h>
13+
LOG_MODULE_REGISTER(EMUL_LC709203F);
14+
15+
#include <zephyr/device.h>
16+
#include <zephyr/drivers/emul.h>
17+
#include <zephyr/drivers/i2c.h>
18+
#include <zephyr/drivers/i2c_emul.h>
19+
#include <zephyr/sys/byteorder.h>
20+
#include <zephyr/sys/crc.h>
21+
22+
#include "lc709203f.h"
23+
24+
/* You can store as many registers as you need.
25+
* Note: The LC709203F typically uses 16-bit registers.
26+
*/
27+
struct lc709203f_emul_data {
28+
/* This emulator object (required for i2c_emul). */
29+
struct i2c_emul emul;
30+
/* The I2C emulation config (pointer to our dev_config). */
31+
const struct i2c_emul_api *api;
32+
/* A backing store for registers in the device. */
33+
uint16_t regs[0x1B]; /* or enough to hold all used registers */
34+
};
35+
36+
struct lc709203f_emul_cfg {
37+
/** I2C address of emulator */
38+
uint16_t addr;
39+
};
40+
41+
/* Polynomial to calculate CRC-8-ATM */
42+
#define LC709203F_CRC_POLYNOMIAL 0x07
43+
44+
/* Reset handler (optional). You can reset internal state here if desired. */
45+
static int lc709203f_emul_reset(const struct emul *target)
46+
{
47+
struct lc709203f_emul_data *data = (struct lc709203f_emul_data *)target->data;
48+
49+
memset(data->regs, 0, sizeof(data->regs));
50+
51+
/* Set default values for registers that your real hardware starts with */
52+
data->regs[LC709203F_REG_BEFORE_RSOC] = 0x0000; /* - */
53+
data->regs[LC709203F_REG_THERMISTOR_B] = 0x0D34; /* B -constant */
54+
data->regs[LC709203F_REG_INITIAL_RSOC] = 0x0000; /* - */
55+
data->regs[LC709203F_REG_CELL_TEMPERATURE] = 0x0BA6; /* 25.0 °C 298.2 °K -> */
56+
data->regs[LC709203F_REG_CELL_VOLTAGE] = 3700; /* 3.7 V in mV */
57+
data->regs[LC709203F_REG_CURRENT_DIRECTION] = 0x0000; /* Auto mode */
58+
data->regs[LC709203F_REG_APA] = 0x0000; /* - */
59+
data->regs[LC709203F_REG_APT] = 0x001E; /* initial value */
60+
data->regs[LC709203F_REG_RSOC] = 50; /* 50% battery level */
61+
data->regs[LC709203F_REG_CELL_ITE] = 500; /* 50.0% battery level */
62+
data->regs[LC709203F_REG_IC_VERSION] = 0x1234; /* Example chip ID */
63+
data->regs[LC709203F_REG_BAT_PROFILE] = 0x0000; /* - */
64+
data->regs[LC709203F_REG_ALARM_LOW_RSOC] = 0x0008; /* 8% */
65+
data->regs[LC709203F_REG_ALARM_LOW_VOLTAGE] = 0x0000; /* initial value */
66+
data->regs[LC709203F_REG_IC_POWER_MODE] = 0x0002; /* - */
67+
data->regs[LC709203F_REG_STATUS_BIT] = 0x0000; /* initial value */
68+
data->regs[LC709203F_REG_NUM_PARAMETER] = 0x0301; /* - */
69+
70+
return 0;
71+
}
72+
73+
static int emul_lc709203f_reg_write(const struct emul *target, uint8_t *buf, size_t len)
74+
{
75+
struct lc709203f_emul_data *data = target->data;
76+
const struct lc709203f_emul_cfg *lc709203f_emul_cfg = target->cfg;
77+
const uint8_t reg = buf[0];
78+
const uint16_t value = sys_get_le16(&buf[1]);
79+
const uint8_t crc = buf[3];
80+
uint8_t crc_buf[4];
81+
82+
crc_buf[0] = lc709203f_emul_cfg->addr << 1;
83+
crc_buf[1] = reg;
84+
crc_buf[2] = buf[1];
85+
crc_buf[3] = buf[2];
86+
87+
const uint8_t crc_calc = crc8(crc_buf, sizeof(crc_buf), LC709203F_CRC_POLYNOMIAL, 0, false);
88+
89+
if (crc != crc_calc) {
90+
LOG_ERR("CRC mismatch on reg 0x%02x", reg);
91+
return -EIO;
92+
}
93+
94+
switch (reg) {
95+
case LC709203F_REG_RSOC:
96+
data->regs[LC709203F_REG_RSOC] = value;
97+
data->regs[LC709203F_REG_CELL_ITE] = value / 10;
98+
break;
99+
case LC709203F_REG_BEFORE_RSOC:
100+
case LC709203F_REG_THERMISTOR_B:
101+
case LC709203F_REG_INITIAL_RSOC:
102+
case LC709203F_REG_CELL_TEMPERATURE:
103+
case LC709203F_REG_CURRENT_DIRECTION:
104+
case LC709203F_REG_APA:
105+
case LC709203F_REG_APT:
106+
case LC709203F_REG_BAT_PROFILE:
107+
case LC709203F_REG_ALARM_LOW_RSOC:
108+
case LC709203F_REG_ALARM_LOW_VOLTAGE:
109+
case LC709203F_REG_IC_POWER_MODE:
110+
case LC709203F_REG_STATUS_BIT:
111+
data->regs[reg] = value;
112+
break;
113+
default:
114+
LOG_ERR("Unknown or read only register 0x%x write", reg);
115+
return -EIO;
116+
}
117+
118+
LOG_INF("write 0x%x", reg);
119+
return 0;
120+
}
121+
122+
static int emul_lc709203f_reg_read(const struct emul *target, int reg, uint8_t *buf, size_t len)
123+
{
124+
struct lc709203f_emul_data *data = target->data;
125+
const struct lc709203f_emul_cfg *lc709203f_emul_cfg = target->cfg;
126+
uint16_t val = 0;
127+
128+
switch (reg) {
129+
case LC709203F_REG_CELL_TEMPERATURE:
130+
if (data->regs[LC709203F_REG_STATUS_BIT] == 0x0000) {
131+
LOG_ERR("Temperature obtaining method is not set to Thermistor mode, "
132+
"instead its set to I2C mode");
133+
return -EIO;
134+
}
135+
case LC709203F_REG_THERMISTOR_B:
136+
case LC709203F_REG_CELL_VOLTAGE:
137+
case LC709203F_REG_CURRENT_DIRECTION:
138+
case LC709203F_REG_APA:
139+
case LC709203F_REG_APT:
140+
case LC709203F_REG_RSOC:
141+
case LC709203F_REG_CELL_ITE:
142+
case LC709203F_REG_IC_VERSION:
143+
case LC709203F_REG_BAT_PROFILE:
144+
case LC709203F_REG_ALARM_LOW_RSOC:
145+
case LC709203F_REG_ALARM_LOW_VOLTAGE:
146+
case LC709203F_REG_IC_POWER_MODE:
147+
case LC709203F_REG_STATUS_BIT:
148+
case LC709203F_REG_NUM_PARAMETER:
149+
val = data->regs[reg];
150+
break;
151+
default:
152+
LOG_ERR("Unknown or write only register 0x%x read", reg);
153+
return -EIO;
154+
}
155+
156+
sys_put_le16(val, buf);
157+
158+
uint8_t crc_buf[5];
159+
160+
/* Build buffer for CRC calculation */
161+
crc_buf[0] = lc709203f_emul_cfg->addr << 1;
162+
crc_buf[1] = reg;
163+
crc_buf[2] = (lc709203f_emul_cfg->addr << 1) | 0x01;
164+
crc_buf[3] = buf[0]; /* LSB */
165+
crc_buf[4] = buf[1]; /* MSB */
166+
167+
/* Calculate CRC and write it into the receive buffer */
168+
buf[2] = crc8(crc_buf, sizeof(crc_buf), LC709203F_CRC_POLYNOMIAL, 0, false);
169+
170+
return 0;
171+
}
172+
173+
static int lc709203f_emul_transfer_i2c(const struct emul *target, struct i2c_msg *msgs,
174+
int num_msgs, int addr)
175+
{
176+
int reg;
177+
178+
__ASSERT_NO_MSG(msgs && num_msgs);
179+
180+
i2c_dump_msgs_rw(target->dev, msgs, num_msgs, addr, false);
181+
switch (num_msgs) {
182+
case 1:
183+
if (msgs->flags & I2C_MSG_READ) {
184+
LOG_ERR("Unexpected read");
185+
return -EIO;
186+
}
187+
188+
if (msgs->len == 4) {
189+
return emul_lc709203f_reg_write(target, msgs->buf, msgs->len);
190+
}
191+
192+
LOG_ERR("Unexpected msg length %d", msgs->len);
193+
return -EIO;
194+
195+
case 2:
196+
if (msgs->flags & I2C_MSG_READ) {
197+
LOG_ERR("Unexpected read");
198+
return -EIO;
199+
}
200+
if (msgs->len != 1) {
201+
LOG_ERR("Unexpected msg0 length %d", msgs->len);
202+
return -EIO;
203+
}
204+
reg = msgs->buf[0];
205+
206+
/* Now process the 'read' part of the message */
207+
msgs++;
208+
if (msgs->flags & I2C_MSG_READ) {
209+
if (msgs->len == 3) {
210+
return emul_lc709203f_reg_read(target, reg, msgs->buf, msgs->len);
211+
}
212+
213+
LOG_ERR("Unexpected msg length %d", msgs->len);
214+
return -EIO;
215+
}
216+
LOG_ERR("Second message must be an I2C write");
217+
return -EIO;
218+
default:
219+
LOG_ERR("Invalid number of messages: %d", num_msgs);
220+
return -EIO;
221+
}
222+
223+
return 0;
224+
}
225+
/* The I2C emulator API required by Zephyr. */
226+
static const struct i2c_emul_api lc709203f_emul_api_i2c = {
227+
.transfer = lc709203f_emul_transfer_i2c,
228+
};
229+
230+
#ifdef CONFIG_ZTEST
231+
#include <zephyr/ztest.h>
232+
233+
/* Add test reset handlers in when using emulators with tests */
234+
#define LC709203F_EMUL_RESET_RULE_BEFORE(inst) lc709203f_emul_reset(EMUL_DT_GET(DT_DRV_INST(inst)));
235+
236+
static void lc709203f_gauge_reset_rule_after(const struct ztest_unit_test *test, void *data)
237+
{
238+
ARG_UNUSED(test);
239+
ARG_UNUSED(data);
240+
241+
DT_INST_FOREACH_STATUS_OKAY(LC709203F_EMUL_RESET_RULE_BEFORE)
242+
}
243+
ZTEST_RULE(lc709203f_gauge_reset, NULL, lc709203f_gauge_reset_rule_after);
244+
#endif /* CONFIG_ZTEST */
245+
246+
/**
247+
* Set up a new emulator (I2C)
248+
*
249+
* @param emul Emulation information
250+
* @param parent Device to emulate
251+
* @return 0 indicating success (always)
252+
*/
253+
static int lc709203f_emul_init(const struct emul *target, const struct device *parent)
254+
{
255+
ARG_UNUSED(parent);
256+
lc709203f_emul_reset(target);
257+
return 0;
258+
}
259+
260+
/*
261+
* Main instantiation macro.
262+
*/
263+
#define DEFINE_LC709203F_EMUL(n) \
264+
static struct lc709203f_emul_data lc709203f_emul_data_##n; \
265+
static const struct lc709203f_emul_cfg lc709203f_emul_cfg_##n = { \
266+
.addr = DT_INST_REG_ADDR(n), \
267+
}; \
268+
EMUL_DT_INST_DEFINE(n, lc709203f_emul_init, &lc709203f_emul_data_##n, \
269+
&lc709203f_emul_cfg_##n, &lc709203f_emul_api_i2c, NULL)
270+
271+
DT_INST_FOREACH_STATUS_OKAY(DEFINE_LC709203F_EMUL);

0 commit comments

Comments
 (0)