Skip to content

Commit 22710b7

Browse files
LostinTimeandspaceYTjhedberg
authored andcommitted
drivers: fuelgauge: Added properties to prop_type.
Adds properties to fuel gauge api to support ADI LTC2959. Signed-off-by: Nathan Winslow <[email protected]>
1 parent 4574678 commit 22710b7

File tree

9 files changed

+1100
-0
lines changed

9 files changed

+1100
-0
lines changed

drivers/fuel_gauge/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ add_subdirectory_ifdef(CONFIG_FUEL_GAUGE_AXP2101 axp2101)
1212
add_subdirectory_ifdef(CONFIG_LC709203F lc709203f)
1313
add_subdirectory_ifdef(CONFIG_SY24561 sy24561)
1414
add_subdirectory_ifdef(CONFIG_BQ40Z50 bq40z50)
15+
add_subdirectory_ifdef(CONFIG_FUEL_GAUGE_LTC2959 ltc2959)
1516

1617
zephyr_library_sources_ifdef(CONFIG_USERSPACE fuel_gauge_syscall_handlers.c)
1718

drivers/fuel_gauge/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,6 @@ source "drivers/fuel_gauge/composite/Kconfig"
2727
source "drivers/fuel_gauge/axp2101/Kconfig"
2828
source "drivers/fuel_gauge/lc709203f/Kconfig"
2929
source "drivers/fuel_gauge/sy24561/Kconfig"
30+
source "drivers/fuel_gauge/ltc2959/Kconfig"
3031

3132
endif # FUEL_GAUGE
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
zephyr_library_sources(ltc2959.c)
2+
3+
zephyr_include_directories_ifdef(CONFIG_EMUL_LTC2959 .)
4+
zephyr_library_sources_ifdef(CONFIG_EMUL_LTC2959 ./emul_ltc2959.c)

drivers/fuel_gauge/ltc2959/Kconfig

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Copyright (c) 2025 Nathan Winslow <[email protected]>
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
config FUEL_GAUGE_LTC2959
6+
depends on DT_HAS_ADI_LTC2959_ENABLED
7+
bool "LTC2959 Fuel Gauge"
8+
default y
9+
select I2C
10+
help
11+
Enable the LTC2959 fuel gauge driver from Analog Devices.
12+
13+
config EMUL_LTC2959
14+
bool "Emulate an LTC2959 fuel gauge"
15+
default y
16+
depends on EMUL
17+
depends on FUEL_GAUGE_LTC2959
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: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
/**
2+
* Copyright (c) 2025 Nathan Winslow
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* Emulator for ltc2959 fuel gauge
6+
*/
7+
8+
#include <string.h>
9+
#include <zephyr/device.h>
10+
#include <zephyr/logging/log.h>
11+
#include <zephyr/drivers/emul.h>
12+
#include <zephyr/drivers/i2c.h>
13+
#include <zephyr/drivers/i2c_emul.h>
14+
#include <zephyr/sys/byteorder.h>
15+
16+
#define DT_DRV_COMPAT adi_ltc2959
17+
18+
LOG_MODULE_REGISTER(EMUL_LTC2959);
19+
20+
#include "ltc2959.h"
21+
22+
struct ltc2959_emul_data {
23+
uint8_t regs[LTC2959_REG_GPIO_THRESH_LOW_LSB + 1]; /* enough for all regs */
24+
};
25+
26+
struct ltc2959_emul_cfg {
27+
/* I2C Address of emulator */
28+
uint16_t addr;
29+
};
30+
31+
static int ltc2959_emul_reset(const struct emul *target)
32+
{
33+
struct ltc2959_emul_data *data = (struct ltc2959_emul_data *)target->data;
34+
35+
memset(data->regs, 0, sizeof(data->regs));
36+
37+
/* Values according to pgs 10-11 of the LTC2959 datasheet */
38+
data->regs[LTC2959_REG_STATUS] = 0x01;
39+
data->regs[LTC2959_REG_ADC_CONTROL] = 0x18;
40+
data->regs[LTC2959_REG_CC_CONTROL] = 0x50;
41+
data->regs[LTC2959_REG_ACC_CHARGE_3] = 0x80;
42+
data->regs[LTC2959_REG_CHG_THRESH_HIGH_3] = 0xFF;
43+
data->regs[LTC2959_REG_CHG_THRESH_HIGH_2] = 0xFF;
44+
data->regs[LTC2959_REG_CHG_THRESH_HIGH_1] = 0xFF;
45+
data->regs[LTC2959_REG_CHG_THRESH_HIGH_0] = 0xFF;
46+
data->regs[LTC2959_REG_VOLT_THRESH_HIGH_MSB] = 0xFF;
47+
data->regs[LTC2959_REG_VOLT_THRESH_HIGH_LSB] = 0xFF;
48+
data->regs[LTC2959_REG_CURR_THRESH_HIGH_MSB] = 0x7F;
49+
data->regs[LTC2959_REG_CURR_THRESH_HIGH_LSB] = 0xFF;
50+
data->regs[LTC2959_REG_CURR_THRESH_LOW_MSB] = 0x80;
51+
data->regs[LTC2959_REG_MAX_CURRENT_MSB] = 0x80;
52+
data->regs[LTC2959_REG_MIN_CURRENT_MSB] = 0x7F;
53+
data->regs[LTC2959_REG_MIN_CURRENT_LSB] = 0xFF;
54+
data->regs[LTC2959_REG_TEMP_THRESH_HIGH_MSB] = 0xFF;
55+
data->regs[LTC2959_REG_TEMP_THRESH_HIGH_LSB] = 0xFF;
56+
data->regs[LTC2959_REG_GPIO_THRESH_HIGH_MSB] = 0x7F;
57+
data->regs[LTC2959_REG_GPIO_THRESH_HIGH_LSB] = 0xFF;
58+
data->regs[LTC2959_REG_GPIO_THRESH_LOW_MSB] = 0x80;
59+
60+
return 0;
61+
}
62+
63+
static int emul_ltc2959_reg_write(const struct emul *target, int reg, int val)
64+
{
65+
struct ltc2959_emul_data *data = target->data;
66+
67+
switch (reg) {
68+
case LTC2959_REG_ADC_CONTROL:
69+
case LTC2959_REG_CC_CONTROL:
70+
case LTC2959_REG_ACC_CHARGE_3:
71+
case LTC2959_REG_ACC_CHARGE_2:
72+
case LTC2959_REG_ACC_CHARGE_1:
73+
case LTC2959_REG_ACC_CHARGE_0:
74+
case LTC2959_REG_CHG_THRESH_LOW_3:
75+
case LTC2959_REG_CHG_THRESH_LOW_2:
76+
case LTC2959_REG_CHG_THRESH_LOW_1:
77+
case LTC2959_REG_CHG_THRESH_LOW_0:
78+
case LTC2959_REG_CHG_THRESH_HIGH_3:
79+
case LTC2959_REG_CHG_THRESH_HIGH_2:
80+
case LTC2959_REG_CHG_THRESH_HIGH_1:
81+
case LTC2959_REG_CHG_THRESH_HIGH_0:
82+
case LTC2959_REG_VOLT_THRESH_HIGH_MSB:
83+
case LTC2959_REG_VOLT_THRESH_HIGH_LSB:
84+
case LTC2959_REG_VOLT_THRESH_LOW_MSB:
85+
case LTC2959_REG_VOLT_THRESH_LOW_LSB:
86+
case LTC2959_REG_MAX_VOLTAGE_MSB:
87+
case LTC2959_REG_MAX_VOLTAGE_LSB:
88+
case LTC2959_REG_MIN_VOLTAGE_MSB:
89+
case LTC2959_REG_MIN_VOLTAGE_LSB:
90+
case LTC2959_REG_CURR_THRESH_HIGH_MSB:
91+
case LTC2959_REG_CURR_THRESH_HIGH_LSB:
92+
case LTC2959_REG_CURR_THRESH_LOW_MSB:
93+
case LTC2959_REG_CURR_THRESH_LOW_LSB:
94+
case LTC2959_REG_MAX_CURRENT_MSB:
95+
case LTC2959_REG_MAX_CURRENT_LSB:
96+
case LTC2959_REG_MIN_CURRENT_MSB:
97+
case LTC2959_REG_MIN_CURRENT_LSB:
98+
case LTC2959_REG_TEMP_THRESH_HIGH_MSB:
99+
case LTC2959_REG_TEMP_THRESH_HIGH_LSB:
100+
case LTC2959_REG_TEMP_THRESH_LOW_MSB:
101+
case LTC2959_REG_TEMP_THRESH_LOW_LSB:
102+
case LTC2959_REG_GPIO_THRESH_HIGH_MSB:
103+
case LTC2959_REG_GPIO_THRESH_HIGH_LSB:
104+
case LTC2959_REG_GPIO_THRESH_LOW_MSB:
105+
case LTC2959_REG_GPIO_THRESH_LOW_LSB:
106+
data->regs[reg] = val;
107+
break;
108+
109+
case LTC2959_REG_STATUS:
110+
case LTC2959_REG_VOLTAGE_MSB:
111+
case LTC2959_REG_VOLTAGE_LSB:
112+
case LTC2959_REG_CURRENT_MSB:
113+
case LTC2959_REG_CURRENT_LSB:
114+
case LTC2959_REG_TEMP_MSB:
115+
case LTC2959_REG_TEMP_LSB:
116+
case LTC2959_REG_GPIO_VOLTAGE_MSB:
117+
case LTC2959_REG_GPIO_VOLTAGE_LSB:
118+
default:
119+
LOG_ERR("Unknown or Read Only Register: 0x%x", reg);
120+
return -EIO;
121+
}
122+
return 0;
123+
}
124+
125+
static int emul_ltc2959_reg_read(const struct emul *target, int reg, int *val)
126+
{
127+
if (reg < LTC2959_REG_STATUS || reg > LTC2959_REG_GPIO_THRESH_LOW_LSB) {
128+
LOG_ERR("Unknown Register: 0x%x", reg);
129+
return -EIO;
130+
}
131+
132+
struct ltc2959_emul_data *data = target->data;
133+
*val = data->regs[reg];
134+
135+
return 0;
136+
}
137+
138+
static int ltc2959_emul_transfer_i2c(const struct emul *target, struct i2c_msg *msgs, int num_msgs,
139+
int addr)
140+
{
141+
__ASSERT_NO_MSG(msgs && num_msgs);
142+
i2c_dump_msgs_rw(target->dev, msgs, num_msgs, addr, false);
143+
144+
switch (num_msgs) {
145+
case 1: {
146+
/* Single write: [reg, data0, data1, ...] */
147+
struct i2c_msg *m = &msgs[0];
148+
149+
if (m->flags & I2C_MSG_READ) {
150+
LOG_ERR("Unexpected single-message read");
151+
return -EIO;
152+
}
153+
if (m->len < 2) {
154+
LOG_ERR("Single-message write must be reg+data (len=%d)", m->len);
155+
return -EIO;
156+
}
157+
uint8_t reg = m->buf[0];
158+
159+
for (size_t i = 1; i < m->len; i++, reg++) {
160+
int ret = emul_ltc2959_reg_write(target, reg, m->buf[i]);
161+
162+
if (ret < 0) {
163+
return ret;
164+
}
165+
}
166+
return 0;
167+
}
168+
169+
case 2: {
170+
/* Two-message: [reg], then [read N] OR [write N] */
171+
struct i2c_msg *m0 = &msgs[0];
172+
struct i2c_msg *m1 = &msgs[1];
173+
174+
if ((m0->flags & I2C_MSG_READ) || m0->len != 1) {
175+
LOG_ERR("Invalid first msg (flags=0x%x len=%d)", m0->flags, m0->len);
176+
return -EIO;
177+
}
178+
179+
uint8_t reg = m0->buf[0];
180+
181+
if (m1->flags & I2C_MSG_READ) {
182+
/* Burst READ: stream N bytes starting at reg */
183+
for (size_t i = 0; i < m1->len; i++) {
184+
int val;
185+
int ret = emul_ltc2959_reg_read(target, reg + i, &val);
186+
187+
if (ret < 0) {
188+
return ret;
189+
}
190+
191+
m1->buf[i] = (uint8_t)val;
192+
}
193+
return 0;
194+
}
195+
/* Burst WRITE: stream N bytes into reg..reg+N-1 */
196+
if (!m1->len) {
197+
LOG_ERR("Empty write");
198+
return -EIO;
199+
}
200+
for (size_t i = 0; i < m1->len; i++) {
201+
int ret = emul_ltc2959_reg_write(target, reg + i, m1->buf[i]);
202+
203+
if (ret < 0) {
204+
return ret;
205+
}
206+
}
207+
return 0;
208+
}
209+
210+
default:
211+
LOG_ERR("Unsupported number of I2C messages: %d", num_msgs);
212+
return -EIO;
213+
}
214+
}
215+
216+
/* The I2C emulator API required by Zephyr. */
217+
static const struct i2c_emul_api ltc2959_emul_api_i2c = {
218+
.transfer = ltc2959_emul_transfer_i2c,
219+
};
220+
221+
#ifdef CONFIG_ZTEST
222+
#include <zephyr/ztest.h>
223+
224+
/* Add test reset handlers in when using emulators with tests */
225+
#define LTC2959_EMUL_RESET_RULE_BEFORE(inst) ltc2959_emul_reset(EMUL_DT_GET(DT_DRV_INST(inst)));
226+
227+
static void ltc2959_gauge_reset_rule_after(const struct ztest_unit_test *test, void *data)
228+
{
229+
ARG_UNUSED(test);
230+
ARG_UNUSED(data);
231+
232+
DT_INST_FOREACH_STATUS_OKAY(LTC2959_EMUL_RESET_RULE_BEFORE)
233+
}
234+
ZTEST_RULE(ltc2959_gauge_reset, NULL, ltc2959_gauge_reset_rule_after);
235+
#endif /* CONFIG_ZTEST */
236+
237+
static int ltc2959_emul_init(const struct emul *target, const struct device *parent)
238+
{
239+
ARG_UNUSED(parent);
240+
ltc2959_emul_reset(target);
241+
return 0;
242+
}
243+
244+
/*
245+
* Main instantiation macro.
246+
*/
247+
#define DEFINE_LTC2959_EMUL(n) \
248+
static struct ltc2959_emul_data ltc2959_emul_data_##n; \
249+
static const struct ltc2959_emul_cfg ltc2959_emul_cfg_##n = { \
250+
.addr = DT_INST_REG_ADDR(n), \
251+
}; \
252+
EMUL_DT_INST_DEFINE(n, ltc2959_emul_init, &ltc2959_emul_data_##n, &ltc2959_emul_cfg_##n, \
253+
&ltc2959_emul_api_i2c, NULL)
254+
255+
DT_INST_FOREACH_STATUS_OKAY(DEFINE_LTC2959_EMUL);

0 commit comments

Comments
 (0)