Skip to content

drivers: fuelguage: Add ADI LTC2959 driver #90356

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

LostinTimeandspaceYT
Copy link

The LTC®2959 is an ultra-low power battery gas gauge
that accurately measures charge, voltage, current and
temperature. Its wide input voltage range and its low
operating current make the LTC2959 suitable for many
applications, including duty-cycled systems that operate
over long lifetime.

This driver integrates with Zephyr’s fuel gauge subsystem to provide access to telemetry data such as voltage, current, temperature, and accumulated charge.

Tested on custom hardware with known RSENSE configuration and verified against datasheet behavior.

Signed-off-by: Nathan Winslow natelostintimeandspace@gmail.com

Signed-off-by: Nathan <natelostintimeandspace@gmail.com>
Copy link

Hello @LostinTimeandspaceYT, and thank you very much for your first pull request to the Zephyr project!
Our Continuous Integration pipeline will execute a series of checks on your Pull Request commit messages and code, and you are expected to address any failures by updating the PR. Please take a look at our commit message guidelines to find out how to format your commit messages, and at our contribution workflow to understand how to update your Pull Request. If you haven't already, please make sure to review the project's Contributor Expectations and update (by amending and force-pushing the commits) your pull request if necessary.
If you are stuck or need help please join us on Discord and ask your question there. Additionally, you can escalate the review when applicable. 😊

Nathan added 2 commits May 22, 2025 16:04
Signed-off-by: Nathan <natelostintimeandspace@gmail.com>
Signed-off-by: Nathan <natelostintimeandspace@gmail.com>
@@ -0,0 +1,3 @@
zephyr_library()
zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR})
zephyr_library_sources(ltc2959.c)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the driver itself is missing.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My apologies. I was pushing the driver itself in a separate commit.

Signed-off-by: Nathan <natelostintimeandspace@gmail.com>
@fmoessbauer
Copy link
Contributor

IMHO this should go after #90310 (or at least 12a8422) so things can be tested with the generic example.

Signed-off-by: Nathan <natelostintimeandspace@gmail.com>
@kartben kartben requested a review from Copilot May 23, 2025 07:06
Copilot

This comment was marked as off-topic.

Signed-off-by: Nathan Winslow <natelostintimeandspace@gmail.com>
Copy link

@ttmut
Copy link
Contributor

ttmut commented Jul 21, 2025

Hi @LostinTimeandspaceYT,

Thanks for your contribution. Please refactor your commit title and body so that they adhere to Pull Request Guidelines.

See these examples:

You should also merge your commits into a single commit to retain bisectability as described in PR Requirements

Do not forget to address the compliance failures as well: https://github.com/zephyrproject-rtos/zephyr/actions/runs/15276958086/job/42967094398?pr=90356

@LostinTimeandspaceYT LostinTimeandspaceYT changed the title drivers: fuel_guage: Add support for Analog Devices LTC2959 drivers: fuelguage: Add ADI LTC2959 driver Jul 21, 2025
Comment on lines +16 to +23
config FUEL_GAUGE_LTC2959_RSENSE_MOHMS

depends on FUEL_GAUGE_LTC2959
int "Sense resistor value in milliohms"
default 80
help
Set the value of the RSENSE resistor in milliohms.
This is used to calculate the current and charge resolution.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a hardware parameter, so I suggest converting it into a devicetree property. See https://docs.zephyrproject.org/latest/build/dts/dt-vs-kconfig.html.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I will move this into the binding.

Comment on lines +6 to +13
config FUEL_GAUGE_LTC2959

depends on DT_HAS_ADI_LTC2959_ENABLED
bool "LTC2959 Fuel Gauge"
default y
select I2C
help
Enable the LTC2959 fuel gauge driver from Analog Devices.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
config FUEL_GAUGE_LTC2959
depends on DT_HAS_ADI_LTC2959_ENABLED
bool "LTC2959 Fuel Gauge"
default y
select I2C
help
Enable the LTC2959 fuel gauge driver from Analog Devices.
config FUEL_GAUGE_LTC2959
depends on DT_HAS_ADI_LTC2959_ENABLED
bool "LTC2959 Fuel Gauge"
default y
select I2C
help
Enable the LTC2959 fuel gauge driver from Analog Devices.

const struct ltc2959_config *cfg = dev->config;
mask &= LTC2959_CC_WRITABLE_MASK;
mask |= LTC2959_CC_RESERVED_VALUE;
LOG_INF("setting cc to: 0x%02X", mask);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
LOG_INF("setting cc to: 0x%02X", mask);
LOG_DBG("setting cc to: 0x%02X", mask);

int ret = ltc2959_read16(dev, reg, &raw);

if (ret < 0) {
LOG_ERR("ERROR: %i", ret);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could do with a verbose error message here.

Comment on lines +452 to +469
#if 0
static int ltc2959_get_props(const struct device *dev,
fuel_gauge_prop_t *props,
union fuel_gauge_prop_val *vals,
size_t len)
{

int ret;
for (size_t i = 0; i < len; ++i) {
ret = ltc2959_get_prop(dev, props[i], &vals[i]);
if (ret < 0) {
return ret;
}
}
return 0;

}
#endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not introduce dead code.

Suggested change
#if 0
static int ltc2959_get_props(const struct device *dev,
fuel_gauge_prop_t *props,
union fuel_gauge_prop_val *vals,
size_t len)
{
int ret;
for (size_t i = 0; i < len; ++i) {
ret = ltc2959_get_prop(dev, props[i], &vals[i]);
if (ret < 0) {
return ret;
}
}
return 0;
}
#endif

Comment on lines +16 to +17
label:
required: true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where do you use this?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was being used in test code to verify the binding. I will remove this property

Comment on lines +19 to +24
alert-gpios:
type: phandle-array
description: |
Optional GPIO used for ALERT output. The LTC2959 pulls this pin low
when thresholds are crossed or charge events occur. Typically wired
to a host interrupt-capable GPIO pin.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not seem to be used anywhere.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ttmut I will create a custom property to access the state of this pin.

@zephyrbot zephyrbot requested a review from DBS06 July 21, 2025 13:36
Signed-off-by: Nathan <natelostintimeandspace@gmail.com>
Copy link

@LostinTimeandspaceYT
Copy link
Author

Thank you @ttmut for reviewing this PR. I will have time later this week to work on the needed changes and consolidate the commits as per the guidelines.

Copy link
Contributor

@DBS06 DBS06 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First of all, thanks for your contribution and overall it looks well done!
I also looked inside the datasheet and as far as I see you implemented every necessary/interesting feature from the IC. Interestingly, the IC does not provide a relative state of charge in percent, but it is what it is.

Please also add an e.g. ltc2959-emulation, for example emul_lc709203f.c, emul_max17048, or emul_bq27z746.c and also a test (look here), because this helps a lot to avoid possible future breaking changes and further increase the test coverage and quality of Zephyr.

/* Used when ACR is controlled via firmware */
#define LTC2959_ACR_CLR (0xFFFFFFFF)

#ifdef CONFIG_FUEL_GAUGE_LTC2959_RSENSE_MOHMS
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be done via Devicetree as stated here can be removed here

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, these are helpful examples. I will add an emul and ztest.

/* LTC2959 Register Map — from datasheet (Rev A) */

/* Status and Control */
#define LTC2959_REG_STATUS (0x00)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to put all registers in an enum, see here for example.
Furthermore, except the enum ltc2959_custom_props and the registers, everything else (the constant values even including the struct ltc2959_config , etc.) can/should be moved to the ltc2959.c, because there is no need to have it in the public interface.


int ret = i2c_burst_read_dt(&cfg->i2c, reg, buf, sizeof(buf));

if (ret < 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (ret < 0) {
if (ret) {

but both ways are valid

Comment on lines +211 to +212
case LTC2959_GPIO_MODE_ALERT:
case LTC2959_GPIO_MODE_CHGCOMP:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
case LTC2959_GPIO_MODE_ALERT:
case LTC2959_GPIO_MODE_CHGCOMP:

If they are doing nothing, the default: branch is enough.

Comment on lines +169 to +174
FUEL_GAUGE_VOLTAGE_HIGH_THRESHOLD,
FUEL_GAUGE_VOLTAGE_LOW_THRESHOLD,
FUEL_GAUGE_CURRENT_HIGH_THRESHOLD,
FUEL_GAUGE_CURRENT_LOW_THRESHOLD,
FUEL_GAUGE_TEMPERATURE_HIGH_THRESHOLD,
FUEL_GAUGE_TEMPERATURE_LOW_THRESHOLD,
Copy link
Contributor

@DBS06 DBS06 Jul 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For FUEL_GAUGE_VOLTAGE_LOW_THRESHOLD the FUEL_GAUGE_LOW_VOLTAGE_ALARM property already exists.
But nevertheless, I think it is valid to add the other 5 properties to the fuel_gauge.h interface, for me it seems legit to add them, because if we already have a low-voltage-alarm why not have a high-voltage-alarm.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I elected not to use FUEL_GAUGE_LOW_VOLTAGE_ALARM as that reports a percentage of charge. The purpose of these properties is for configuring bounds to trigger a fault in the status register. I am not sure how other ICs of this ilk operate, but I agree @DBS06, I think adding them would make sense.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@LostinTimeandspaceYT FUEL_GAUGE_LOW_VOLTAGE_ALARM is described in fuel_gauge.h as /** Low Cell Voltage Alarm (uV)*/ so it does not "reports a percentage of charge", I think you mismatch that with FUEL_GAUGE_STATE_OF_CHARGE_ALARM which is described as /** Remaining state of charge alarm (percent, 0-100) */.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DBS06 You're right I did, classic off-by-1 😅
image

I could change the current FUEL_GAUGE_LOW_XXXX_THRESHOLD to FUEL_GAUGE_LOW_XXX_ALARM to have the property names be consistent with the current API.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could change the current FUEL_GAUGE_LOW_XXXX_THRESHOLD to FUEL_GAUGE_LOW_XXX_ALARM to have the property names be consistent with the current API.

Exactly what I meant! You can keep the register names as it is and just adopt the property names (they are more or less generic names).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started work on this driver in a project on v4.1.0 and it looks like the FUEL_GAUGE_LOW_VOLTAGE_ALARM property was added after that release. I noticed the discrepancy when attempting to build in that project. I Apologize for the confusion.

Copy link
Contributor

@DBS06 DBS06 Jul 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to apologize! That's why code reviews are for.

Yes, I added it after my last PR #90310, which went bigger than originally expected 🤔

Just a minor hint, if you are adding additional properties do that in a separate commit (look at the commits from here #90310) and it should be the first one, as far as I see you can squash all other currently existing commits into one commit. If you add a test for your driver do that in another commit. So basically in the end you have three commits which will be pushed in the main branch.

I hope this helps!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants