Browse Source

fuel_gauge: add basic support for AXP2101 chip

The AXP2101 chip is a multi functional power chip offering a regulator,
charge controller and a fuel gauge (battery percentage and voltage).
Hereby, the fuel gauge provides much more reliable data compared to using
an ADC.

We implement minimal support for this chip (connected state, voltage and
gauge) and bind it to the fuel gauge subsystem.

Closes: #89158
Signed-off-by: Felix Moessbauer <felix.moessbauer@gmail.com>
pull/90115/head
Felix Moessbauer 2 months ago committed by Benjamin Cabé
parent
commit
84ebbdcd4b
  1. 1
      drivers/fuel_gauge/CMakeLists.txt
  2. 1
      drivers/fuel_gauge/Kconfig
  3. 4
      drivers/fuel_gauge/axp2101/CMakeLists.txt
  4. 14
      drivers/fuel_gauge/axp2101/Kconfig
  5. 203
      drivers/fuel_gauge/axp2101/fuel_gauge_axp2101.c
  6. 9
      dts/bindings/fuel-gauge/x-powers,axp2101-fuel-gauge.yaml

1
drivers/fuel_gauge/CMakeLists.txt

@ -6,6 +6,7 @@ add_subdirectory_ifdef(CONFIG_SBS_GAUGE_NEW_API sbs_gauge) @@ -6,6 +6,7 @@ add_subdirectory_ifdef(CONFIG_SBS_GAUGE_NEW_API sbs_gauge)
add_subdirectory_ifdef(CONFIG_FUEL_GAUGE_COMPOSITE composite)
add_subdirectory_ifdef(CONFIG_MAX17048 max17048)
add_subdirectory_ifdef(CONFIG_BQ27Z746 bq27z746)
add_subdirectory_ifdef(CONFIG_FUEL_GAUGE_AXP2101 axp2101)
zephyr_library_sources_ifdef(CONFIG_USERSPACE fuel_gauge_syscall_handlers.c)

1
drivers/fuel_gauge/Kconfig

@ -23,5 +23,6 @@ source "drivers/fuel_gauge/max17048/Kconfig" @@ -23,5 +23,6 @@ source "drivers/fuel_gauge/max17048/Kconfig"
source "drivers/fuel_gauge/sbs_gauge/Kconfig"
source "drivers/fuel_gauge/bq27z746/Kconfig"
source "drivers/fuel_gauge/composite/Kconfig"
source "drivers/fuel_gauge/axp2101/Kconfig"
endif # FUEL_GAUGE

4
drivers/fuel_gauge/axp2101/CMakeLists.txt

@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
# SPDX-License-Identifier: Apache-2.0
zephyr_library()
zephyr_library_sources(fuel_gauge_axp2101.c)

14
drivers/fuel_gauge/axp2101/Kconfig

@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
# Zephyr axp2101 fuel-gauge device
# Copyright (c) 2025 Felix Moessbauer
# SPDX-License-Identifier: Apache-2.0
config FUEL_GAUGE_AXP2101
bool "X-Powers AXP2101 fuel gauge"
default y
depends on DT_HAS_X_POWERS_AXP2101_FUEL_GAUGE_ENABLED
depends on DT_HAS_X_POWERS_AXP2101_ENABLED
select I2C
select MFD
help
Enable driver for the x-powers axp2101 fuel gauge device.

203
drivers/fuel_gauge/axp2101/fuel_gauge_axp2101.c

@ -0,0 +1,203 @@ @@ -0,0 +1,203 @@
/*
* Copyright (c) 2025, Felix Moessbauer
*
* SPDX-License-Identifier: Apache-2.0
*
* Note: The functions that query the raw values via i2c are named similar
* to the ones in the reference implementation in the XPowersLib. This also
* applies to the register names (defines).
*
* Datasheet:
* https://github.com/lewisxhe/XPowersLib/blob/a7d06b98c1136c8fee7854b1d29a9f012b2aba83/datasheet/AXP2101_Datasheet_V1.0_en.pdf
*/
#define DT_DRV_COMPAT x_powers_axp2101_fuel_gauge
#include <zephyr/device.h>
#include <zephyr/drivers/fuel_gauge.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(fuel_gauge_axp2101, CONFIG_FUEL_GAUGE_LOG_LEVEL);
/* clang-format off */
/* registers */
#define AXP2101_STATUS1 0x00
#define BAT_PRESENT_MASK BIT(3)
#define AXP2101_CHARGE_GAUGE_WDT_CTRL 0x18
#define GAUGE_ENABLE_MASK BIT(3)
#define AXP2101_ADC_DATA_VBAT_H 0x34
#define GAUGE_VBAT_H_MASK 0x1F
#define AXP2101_ADC_DATA_VBAT_L 0x35
#define AXP2101_BAT_DET_CTRL 0x68
#define BAT_TYPE_DET_MASK BIT(0)
#define AXP2101_BAT_PERCENT_DATA 0xA4
/* internal feature flags */
#define GAUGE_FEATURE_BAT_DET BIT(0)
#define GAUGE_FEATURE_GAUGE BIT(1)
#define GAUGE_FEATURE_ALL (GAUGE_FEATURE_BAT_DET | GAUGE_FEATURE_GAUGE)
/* clang-format on */
struct axp2101_config {
struct i2c_dt_spec i2c;
};
struct axp2101_data {
uint8_t features;
};
static int enable_fuel_gauge(const struct device *dev)
{
const struct axp2101_config *cfg = dev->config;
struct axp2101_data *data = dev->data;
int ret = 0;
ret = i2c_reg_update_byte_dt(&cfg->i2c, AXP2101_CHARGE_GAUGE_WDT_CTRL, GAUGE_ENABLE_MASK,
GAUGE_ENABLE_MASK);
if (ret < 0) {
data->features &= ~GAUGE_FEATURE_GAUGE;
}
return ret;
}
static int enable_batt_detection(const struct device *dev)
{
const struct axp2101_config *cfg = dev->config;
struct axp2101_data *data = dev->data;
int ret = 0;
ret = i2c_reg_update_byte_dt(&cfg->i2c, AXP2101_BAT_DET_CTRL, BAT_TYPE_DET_MASK,
BAT_TYPE_DET_MASK);
if (ret < 0) {
data->features &= ~GAUGE_FEATURE_BAT_DET;
}
return ret;
}
static int is_battery_connect(const struct device *dev, union fuel_gauge_prop_val *val)
{
const struct axp2101_config *cfg = dev->config;
struct axp2101_data *data = dev->data;
uint8_t tmp;
int ret;
if ((data->features & GAUGE_FEATURE_BAT_DET) == 0) {
return -ENOTSUP;
}
ret = i2c_reg_read_byte_dt(&cfg->i2c, AXP2101_STATUS1, &tmp);
if (ret < 0) {
return ret;
}
val->present_state = tmp & BAT_PRESENT_MASK;
return 0;
}
static int get_battery_percent(const struct device *dev, union fuel_gauge_prop_val *val)
{
const struct axp2101_config *cfg = dev->config;
struct axp2101_data *data = dev->data;
uint8_t tmp;
int ret;
if ((data->features & GAUGE_FEATURE_GAUGE) == 0) {
return -ENOTSUP;
}
ret = i2c_reg_read_byte_dt(&cfg->i2c, AXP2101_BAT_PERCENT_DATA, &tmp);
if (ret < 0) {
return ret;
}
val->relative_state_of_charge = tmp;
return 0;
}
static int get_bat_voltage(const struct device *dev, union fuel_gauge_prop_val *val)
{
const struct axp2101_config *cfg = dev->config;
struct axp2101_data *data = dev->data;
uint8_t h5, l8;
int ret;
if ((data->features & GAUGE_FEATURE_GAUGE) == 0) {
return -ENOTSUP;
}
ret = i2c_reg_read_byte_dt(&cfg->i2c, AXP2101_ADC_DATA_VBAT_H, &h5);
if (ret < 0) {
return ret;
}
ret = i2c_reg_read_byte_dt(&cfg->i2c, AXP2101_ADC_DATA_VBAT_L, &l8);
if (ret < 0) {
return ret;
}
val->voltage = (((h5 & GAUGE_VBAT_H_MASK) << 8) | l8) * 1000;
return 0;
}
static int axp2101_get_prop(const struct device *dev, fuel_gauge_prop_t prop,
union fuel_gauge_prop_val *val)
{
switch (prop) {
case FUEL_GAUGE_PRESENT_STATE:
case FUEL_GAUGE_CONNECT_STATE:
return is_battery_connect(dev, val);
case FUEL_GAUGE_VOLTAGE:
return get_bat_voltage(dev, val);
case FUEL_GAUGE_ABSOLUTE_STATE_OF_CHARGE:
case FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE:
return get_battery_percent(dev, val);
default:
return -ENOTSUP;
}
}
static int axp2101_init(const struct device *dev)
{
struct axp2101_data *data = dev->data;
const struct axp2101_config *cfg;
int ret = 0;
cfg = dev->config;
if (!device_is_ready(cfg->i2c.bus)) {
LOG_ERR("Bus device is not ready");
return -ENODEV;
}
ret = enable_fuel_gauge(dev);
if (ret < 0) {
LOG_WRN("Failed to enable fuel gauge");
data->features &= ~GAUGE_FEATURE_GAUGE;
}
ret = enable_batt_detection(dev);
if (ret < 0) {
LOG_WRN("Failed to enable battery detection");
data->features &= ~GAUGE_FEATURE_BAT_DET;
}
return 0;
}
static DEVICE_API(fuel_gauge, axp2101_api) = {
.get_property = axp2101_get_prop,
};
#define AXP2101_INIT(inst) \
static const struct axp2101_config axp2101_config_##inst = { \
.i2c = I2C_DT_SPEC_GET(DT_PARENT(DT_INST(inst, DT_DRV_COMPAT))), \
}; \
static struct axp2101_data axp2101_data_##inst = { \
.features = GAUGE_FEATURE_ALL, \
}; \
DEVICE_DT_INST_DEFINE(inst, &axp2101_init, NULL, &axp2101_data_##inst, \
&axp2101_config_##inst, POST_KERNEL, \
CONFIG_FUEL_GAUGE_INIT_PRIORITY, &axp2101_api);
DT_INST_FOREACH_STATUS_OKAY(AXP2101_INIT)

9
dts/bindings/fuel-gauge/x-powers,axp2101-fuel-gauge.yaml

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
# Copyright (c) 2025 Felix Moessbauer <felix.moessbauer@gmail.com>
# SPDX-License-Identifier: Apache-2.0
description: |
X-Powers AXP2101 Fuel Gauge
compatible: "x-powers,axp2101-fuel-gauge"
include: [fuel-gauge.yaml]
Loading…
Cancel
Save