blob: 9b50b7bc133263b8d921849747741c7ec2685730 [file]
/*
* Copyright (c) 2026 Analog Devices Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/charger.h>
#include <zephyr/drivers/fuel_gauge.h>
#include <zephyr/drivers/regulator.h>
#include <sys/errno.h>
#include <stdio.h>
#include <string.h>
#include <zephyr/sys/__assert.h>
#define DEVICE_NAME_CHARGER "charger"
#define DEVICE_NAME_FUEL_GAUGE "fuel gauge"
#define DEVICE_NAME_BUCK_REGULATOR "buck regulator"
#define DEVICE_NAME_BUCK_BOOST_REGULATOR "buck boost regulator"
static const char *now_str(void)
{
static char buf[16]; /* ...HH:MM:SS.MMM */
uint32_t now = k_uptime_get_32();
unsigned int ms = now % MSEC_PER_SEC;
unsigned int s;
unsigned int min;
unsigned int h;
now /= MSEC_PER_SEC;
s = now % 60U;
now /= 60U;
min = now % 60U;
now /= 60U;
h = now;
snprintf(buf, sizeof(buf), "%u:%02u:%02u.%03u", h, min, s, ms);
return buf;
}
static int check_device_ready(const struct device *dev, const char *dev_name)
{
if (dev == NULL) {
printk("Error: no %s device found.\n", dev_name);
return -1;
}
if (!device_is_ready(dev)) {
printk("Error: Device \"%s\" is not ready; "
"check the driver initialization logs for errors.\n",
dev->name);
return -1;
}
return 0;
}
static int read_charger_properties(const struct device *chgdev)
{
union charger_propval charger_val;
int ret;
printk("[%s]: Found device \"%s\", getting charger data\n", now_str(), chgdev->name);
ret = charger_get_prop(chgdev, CHARGER_PROP_CONSTANT_CHARGE_CURRENT_UA, &charger_val);
if (ret) {
printk("[%s]: Error getting Constant charge current on device! ret %d\n", now_str(),
ret);
return -1;
}
printk("[%s]: Current (Fast Charge): %d uA\n", now_str(),
charger_val.const_charge_current_ua);
ret = charger_get_prop(chgdev, CHARGER_PROP_INPUT_REGULATION_VOLTAGE_UV, &charger_val);
if (ret) {
printk("[%s]: Error getting Adaptive Voltage on device! ret %d\n", now_str(), ret);
return -1;
}
printk("[%s]: Adaptive Voltage: %d uV\n", now_str(),
charger_val.input_voltage_regulation_voltage_uv);
ret = charger_get_prop(chgdev, CHARGER_PROP_CHARGE_TERM_CURRENT_UA, &charger_val);
if (ret) {
printk("[%s]: Error getting Termination Current on device! ret %d\n", now_str(),
ret);
return -1;
}
printk("[%s]: Current (Termination): %d uA\n", now_str(),
charger_val.charge_term_current_ua);
return 0;
}
static int enable_charger(const struct device *chgdev)
{
int ret;
ret = charger_charge_enable(chgdev, true);
if (ret) {
printk("[%s]: Failed to start charger! ret %d\n", now_str(), ret);
return -1;
}
return 0;
}
static int read_fuel_gauge_properties(const struct device *flgdev)
{
union fuel_gauge_prop_val fuel_gauge_val;
int ret;
ret = fuel_gauge_get_prop(flgdev, FUEL_GAUGE_DESIGN_CAPACITY, &fuel_gauge_val);
if (ret) {
printk("[%s]: Error getting Fuel Gauge Battery Capacity on device! ret %d\n",
now_str(), ret);
return -1;
}
return 0;
}
static int read_regulator_voltages(const struct device *bckdev, const struct device *bstdev)
{
uint32_t volt_out_uv;
int ret;
ret = regulator_get_voltage(bckdev, &volt_out_uv);
if (ret) {
printk("[%s]: Error getting buck voltage on device! ret %d\n", now_str(), ret);
return -1;
}
printk("[%s]: Buck Output Voltage: %d\n", now_str(), volt_out_uv);
ret = regulator_get_voltage(bstdev, &volt_out_uv);
if (ret) {
printk("[%s]: Error getting buck boost voltage on device! ret %d\n", now_str(),
ret);
return -1;
}
printk("[%s]: Buck Boost Output Voltage: %d\n", now_str(), volt_out_uv);
return 0;
}
static int monitor_charger_status(const struct device *chgdev)
{
union charger_propval charger_val;
int ret;
ret = charger_get_prop(chgdev, CHARGER_PROP_ONLINE, &charger_val);
if (ret) {
printk("[%s]: Error getting Online status on device! ret %d\n", now_str(), ret);
return -1;
}
if (charger_val.online == CHARGER_ONLINE_FIXED) {
printk("[%s]: Online: Charger is active.\n", now_str());
} else {
printk("[%s]: Online: Charger is offline.\n", now_str());
}
ret = charger_get_prop(chgdev, CHARGER_PROP_PRESENT, &charger_val);
if (ret) {
printk("[%s]: Error getting Battery Present status on device! ret %d\n", now_str(),
ret);
return -1;
}
if (charger_val.present) {
printk("[%s]: Present: Battery is Present.\n", now_str());
} else {
printk("[%s]: Present: Battery is Not Present.\n", now_str());
}
ret = charger_get_prop(chgdev, CHARGER_PROP_STATUS, &charger_val);
if (ret) {
printk("[%s]: Error getting Charging Status on device! ret %d\n", now_str(), ret);
return -1;
}
switch (charger_val.status) {
case CHARGER_STATUS_NOT_CHARGING:
printk("[%s]: Status: Charger is Not Charging.\n", now_str());
break;
case CHARGER_STATUS_CHARGING:
printk("[%s]: Status: Charger is Charging.\n", now_str());
break;
case CHARGER_STATUS_FULL:
printk("[%s]: Status: Charging is Complete.\n", now_str());
break;
case CHARGER_STATUS_DISCHARGING:
printk("[%s]: Status: Charger is not charging and in an internal status "
"state.\n",
now_str());
break;
default:
printk("[%s]: Status: Charger Status is unknown.\n", now_str());
}
ret = charger_get_prop(chgdev, CHARGER_PROP_CHARGE_TYPE, &charger_val);
if (ret) {
printk("[%s]: Error getting Charging Mode on device! ret %d\n", now_str(), ret);
return -1;
}
switch (charger_val.charge_type) {
case CHARGER_CHARGE_TYPE_NONE:
printk("[%s]: Mode: Charger is Not Charging.\n", now_str());
break;
case CHARGER_CHARGE_TYPE_TRICKLE:
printk("[%s]: Mode: Charger is in Trickle Mode.\n", now_str());
break;
case CHARGER_CHARGE_TYPE_FAST:
printk("[%s]: Mode: Charging is in Fast Mode (CC).\n", now_str());
break;
case CHARGER_CHARGE_TYPE_LONGLIFE:
printk("[%s]: Mode: Charging is in Fast Mode (CV).\n", now_str());
break;
case CHARGER_CHARGE_TYPE_UNKNOWN:
printk("[%s]: Mode: Charger is not charging and in an internal status "
"state.\n",
now_str());
default:
printk("[%s]: Mode: Charger Type is unknown.\n", now_str());
}
ret = charger_get_prop(chgdev, CHARGER_PROP_HEALTH, &charger_val);
if (ret) {
printk("[%s]: Error getting Battery Health Status on device! ret %d\n", now_str(),
ret);
return -1;
}
switch (charger_val.health) {
case CHARGER_HEALTH_COLD:
printk("[%s]: Health: Battery is Cold.\n", now_str());
break;
case CHARGER_HEALTH_COOL:
printk("[%s]: Health: Battery is Cool.\n", now_str());
break;
case CHARGER_HEALTH_WARM:
printk("[%s]: Health: Battery is Warm.\n", now_str());
break;
case CHARGER_HEALTH_HOT:
printk("[%s]: Health: Battery is Hot.\n", now_str());
break;
case CHARGER_HEALTH_WATCHDOG_TIMER_EXPIRE:
printk("[%s]: Health: Charger Watchdog has expired!\n", now_str());
break;
case CHARGER_HEALTH_OVERVOLTAGE:
printk("[%s]: Health: Charger Overvoltage has occurred!\n", now_str());
break;
case CHARGER_HEALTH_OVERHEAT:
printk("[%s]: Health: Charger is Overheating!\n", now_str());
break;
case CHARGER_HEALTH_SAFETY_TIMER_EXPIRE:
printk("[%s]: Health: Charger safety timer has expired!\n", now_str());
break;
case CHARGER_HEALTH_NO_BATTERY:
printk("[%s]: Health: No battery connected!\n", now_str());
break;
case CHARGER_HEALTH_GOOD:
printk("[%s]: Health: Charger health is good.\n", now_str());
break;
default:
printk("[%s]: Health: Charger health is unknown.\n", now_str());
}
return 0;
}
static int monitor_fuel_gauge_status(const struct device *flgdev)
{
union fuel_gauge_prop_val fuel_gauge_val;
int ret;
ret = fuel_gauge_get_prop(flgdev, FUEL_GAUGE_VOLTAGE_UV, &fuel_gauge_val);
if (ret) {
printk("[%s]: Error getting Fuel Gauge Voltage on device! ret %d\n", now_str(),
ret);
return -1;
}
printk("[%s]: Fuel gauge Voltage: %d\n", now_str(), fuel_gauge_val.voltage_uv);
ret = fuel_gauge_get_prop(flgdev, FUEL_GAUGE_STATUS, &fuel_gauge_val);
if (ret) {
printk("[%s]: Error getting Fuel Gauge Status on device! ret %d\n", now_str(), ret);
return -1;
}
printk("[%s]: Fuel gauge Status 0x%X\n", now_str(), fuel_gauge_val.fg_status);
ret = fuel_gauge_get_prop(flgdev, FUEL_GAUGE_ABSOLUTE_STATE_OF_CHARGE_PCT, &fuel_gauge_val);
if (ret) {
printk("[%s]: Error getting Fuel Gauge State of charge on device! ret %d\n",
now_str(), ret);
return -1;
}
printk("[%s]: Fuel gauge SoC: %d\n", now_str(),
fuel_gauge_val.absolute_state_of_charge_pct);
return 0;
}
static int monitor_loop(const struct device *chgdev, const struct device *flgdev)
{
int ret;
while (1) {
ret = monitor_charger_status(chgdev);
if (ret) {
return ret;
}
printk("\n\n");
ret = monitor_fuel_gauge_status(flgdev);
if (ret) {
return ret;
}
k_sleep(K_SECONDS(1));
}
return 0;
}
int main(void)
{
const struct device *chgdev = DEVICE_DT_GET_OR_NULL(DT_NODELABEL(charger));
const struct device *flgdev = DEVICE_DT_GET_OR_NULL(DT_NODELABEL(fuelgauge));
const struct device *bckdev = DEVICE_DT_GET_OR_NULL(DT_NODELABEL(buck));
const struct device *bstdev = DEVICE_DT_GET_OR_NULL(DT_NODELABEL(buckboost));
int ret;
ret = check_device_ready(chgdev, DEVICE_NAME_CHARGER);
if (ret) {
return ret;
}
ret = check_device_ready(flgdev, DEVICE_NAME_FUEL_GAUGE);
if (ret) {
return ret;
}
ret = check_device_ready(bckdev, DEVICE_NAME_BUCK_REGULATOR);
if (ret) {
return ret;
}
ret = check_device_ready(bstdev, DEVICE_NAME_BUCK_BOOST_REGULATOR);
if (ret) {
return ret;
}
ret = read_charger_properties(chgdev);
if (ret) {
return ret;
}
ret = enable_charger(chgdev);
if (ret) {
return ret;
}
ret = read_fuel_gauge_properties(flgdev);
if (ret) {
return ret;
}
ret = read_regulator_voltages(bckdev, bstdev);
if (ret) {
return ret;
}
ret = monitor_loop(chgdev, flgdev);
if (ret) {
return ret;
}
return 0;
}