Skip to content

Commit

Permalink
fuel_gauge: Sample sbs gauge driver with tests
Browse files Browse the repository at this point in the history
Add a sample sbs gauge driver with feature parity and basic tests
comparison to its sensor counter-part. Includes a simple stub test that is
extended upon.

Signed-off-by: Aaron Massey <aaronmassey@google.com>
  • Loading branch information
aaronemassey authored and nashif committed Nov 19, 2022
1 parent ee6e85c commit 28b8123
Show file tree
Hide file tree
Showing 18 changed files with 370 additions and 1 deletion.
1 change: 1 addition & 0 deletions drivers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,4 @@ add_subdirectory_ifdef(CONFIG_BOARD_XENVM xen)
add_subdirectory_ifdef(CONFIG_MM_DRV mm)
add_subdirectory_ifdef(CONFIG_RESET reset)
add_subdirectory_ifdef(CONFIG_COREDUMP_DEVICE coredump)
add_subdirectory_ifdef(CONFIG_FUEL_GAUGE fuel_gauge)
2 changes: 2 additions & 0 deletions drivers/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,6 @@ source "drivers/coredump/Kconfig"

source "drivers/xen/Kconfig"

source "drivers/fuel_gauge/Kconfig"

endmenu
3 changes: 3 additions & 0 deletions drivers/fuel_gauge/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: Apache-2.0

add_subdirectory_ifdef(CONFIG_SBS_GAUGE_NEW_API sbs_gauge)
18 changes: 18 additions & 0 deletions drivers/fuel_gauge/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright 2022 Google LLC
#
# SPDX-License-Identifier: Apache-2.0

menuconfig FUEL_GAUGE
bool "Battery fuel gauge drivers"
help
Enable battery fuel gauge driver configuration.

if FUEL_GAUGE

module = FUEL_GAUGE
module-str = fuel_gauge
source "subsys/logging/Kconfig.template.log_config"

source "drivers/fuel_gauge/sbs_gauge/Kconfig"

endif # FUEL_GAUGE
3 changes: 3 additions & 0 deletions drivers/fuel_gauge/sbs_gauge/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
zephyr_library()

zephyr_library_sources(sbs_gauge.c)
10 changes: 10 additions & 0 deletions drivers/fuel_gauge/sbs_gauge/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright (c) 2021 Leica Geosystems AG
# SPDX-License-Identifier: Apache-2.0

config SBS_GAUGE_NEW_API
bool "Smart Battery Fuel Gauge"
default y
depends on DT_HAS_SBS_SBS_GAUGE_NEW_API_ENABLED
select I2C
help
Enable I2C-based/SMBus-based driver for a Smart Battery Fuel Gauge.
138 changes: 138 additions & 0 deletions drivers/fuel_gauge/sbs_gauge/sbs_gauge.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* Copyright (c) 2022 Leica Geosystems AG
*
* Copyright 2022 Google LLC
*
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT sbs_sbs_gauge_new_api

#include "sbs_gauge.h"

#include <zephyr/drivers/fuel_gauge.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/byteorder.h>

LOG_MODULE_REGISTER(sbs_gauge);

static int sbs_cmd_reg_read(const struct device *dev, uint8_t reg_addr, uint16_t *val)
{
const struct sbs_gauge_config *cfg;
uint8_t i2c_data[2];
int status;

cfg = dev->config;
status = i2c_burst_read_dt(&cfg->i2c, reg_addr, i2c_data, ARRAY_SIZE(i2c_data));
if (status < 0) {
LOG_ERR("Unable to read register");
return status;
}

*val = sys_get_le16(i2c_data);

return 0;
}

static int sbs_gauge_get_prop(const struct device *dev, struct fuel_gauge_get_property *prop)
{
int rc = 0;
uint16_t val = 0;

switch (prop->property_type) {
case FUEL_GAUGE_AVG_CURRENT:
rc = sbs_cmd_reg_read(dev, SBS_GAUGE_CMD_AVG_CURRENT, &val);
prop->value.avg_current = val * 1000;
break;
case FUEL_GAUGE_CYCLE_COUNT:
rc = sbs_cmd_reg_read(dev, SBS_GAUGE_CMD_CYCLE_COUNT, &val);
prop->value.cycle_count = val;
break;
case FUEL_GAUGE_CURRENT:
rc = sbs_cmd_reg_read(dev, SBS_GAUGE_CMD_CURRENT, &val);
prop->value.current = val * 1000;
break;
case FUEL_GAUGE_FULL_CHARGE_CAPACITY:
rc = sbs_cmd_reg_read(dev, SBS_GAUGE_CMD_FULL_CAPACITY, &val);
prop->value.full_charge_capacity = val * 1000;
break;
case FUEL_GAUGE_REMAINING_CAPACITY:
rc = sbs_cmd_reg_read(dev, SBS_GAUGE_CMD_REM_CAPACITY, &val);
prop->value.remaining_capacity = val * 1000;
break;
case FUEL_GAUGE_RUNTIME_TO_EMPTY:
rc = sbs_cmd_reg_read(dev, SBS_GAUGE_CMD_RUNTIME2EMPTY, &val);
prop->value.runtime_to_empty = val;
break;
case FUEL_GAUGE_RUNTIME_TO_FULL:
rc = sbs_cmd_reg_read(dev, SBS_GAUGE_CMD_AVG_TIME2FULL, &val);
prop->value.runtime_to_empty = val;
break;
case FUEL_GAUGE_STATE_OF_CHARGE:
rc = sbs_cmd_reg_read(dev, SBS_GAUGE_CMD_ASOC, &val);
prop->value.state_of_charge = val;
break;
case FUEL_GAUGE_TEMPERATURE:
rc = sbs_cmd_reg_read(dev, SBS_GAUGE_CMD_TEMP, &val);
prop->value.temperature = val;
break;
case FUEL_GAUGE_VOLTAGE:
rc = sbs_cmd_reg_read(dev, SBS_GAUGE_CMD_VOLTAGE, &val);
prop->value.voltage = val * 1000;
break;
default:
rc = -ENOTSUP;
}

prop->status = rc;

return rc;
}

static int sbs_gauge_get_props(const struct device *dev, struct fuel_gauge_get_property *props,
size_t len)
{
int ret = 0;

for (int i = 0; i < len; i++) {
ret |= sbs_gauge_get_prop(dev, props + i);
}

return ret;
}

/**
* @brief initialize the fuel gauge
*
* @return 0 for success
*/
static int sbs_gauge_init(const struct device *dev)
{
const struct sbs_gauge_config *cfg;

cfg = dev->config;

if (!device_is_ready(cfg->i2c.bus)) {
LOG_ERR("Bus device is not ready");
return -ENODEV;
}

return 0;
}

static const struct battery_driver_api sbs_gauge_driver_api = {
.get_property = &sbs_gauge_get_props,
};

/* FIXME: fix init priority */
#define SBS_GAUGE_INIT(index) \
\
static const struct sbs_gauge_config sbs_gauge_config_##index = { \
.i2c = I2C_DT_SPEC_INST_GET(index), \
}; \
\
DEVICE_DT_INST_DEFINE(index, &sbs_gauge_init, NULL, NULL, &sbs_gauge_config_##index, \
POST_KERNEL, 90, &sbs_gauge_driver_api);

DT_INST_FOREACH_STATUS_OKAY(SBS_GAUGE_INIT)
54 changes: 54 additions & 0 deletions drivers/fuel_gauge/sbs_gauge/sbs_gauge.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright (c) 2021 Leica Geosystems AG
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_DRIVERS_SENSOR_SBS_GAUGE_H_
#define ZEPHYR_DRIVERS_SENSOR_SBS_GAUGE_H_

#include <stdint.h>
#include <zephyr/drivers/i2c.h>

/** Standard Commands */
#define SBS_GAUGE_CMD_MANUFACTURER_ACCESS 0x00 /* ManufacturerAccess */
#define SBS_GAUGE_CMD_REM_CAPACITY_ALARM 0x01 /* LowCapacityAlarmThreshold */
#define SBS_GAUGE_CMD_REM_TIME_ALARM 0x02 /* RemainingTimeToEmptyThreshold */
#define SBS_GAUGE_CMD_BATTERY_MODE 0x03 /* BatteryOperatingMode */
#define SBS_GAUGE_CMD_AR 0x04 /* AtRate */
#define SBS_GAUGE_CMD_ARTTF 0x05 /* AtRateTimeToFull */
#define SBS_GAUGE_CMD_ARTTE 0x06 /* AtRateTimeToEmpty */
#define SBS_GAUGE_CMD_AROK 0x07 /* AtRateOK */
#define SBS_GAUGE_CMD_TEMP 0x08 /* Temperature */
#define SBS_GAUGE_CMD_VOLTAGE 0x09 /* Voltage */
#define SBS_GAUGE_CMD_CURRENT 0x0A /* Current */
#define SBS_GAUGE_CMD_AVG_CURRENT 0x0B /* AverageCurrent */
#define SBS_GAUGE_CMD_MAX_ERROR 0x0C /* MaxError */
#define SBS_GAUGE_CMD_RSOC 0x0D /* RelativeStateOfCharge */
#define SBS_GAUGE_CMD_ASOC 0x0E /* AbsoluteStateOfCharge */
#define SBS_GAUGE_CMD_REM_CAPACITY 0x0F /* RemainingCapacity */
#define SBS_GAUGE_CMD_FULL_CAPACITY 0x10 /* FullChargeCapacity */
#define SBS_GAUGE_CMD_RUNTIME2EMPTY 0x11 /* RunTimeToEmpty */
#define SBS_GAUGE_CMD_AVG_TIME2EMPTY 0x12 /* AverageTimeToEmpty */
#define SBS_GAUGE_CMD_AVG_TIME2FULL 0x13 /* AverageTimeToFull */
#define SBS_GAUGE_CMD_CHG_CURRENT 0x14 /* ChargeCurrent */
#define SBS_GAUGE_CMD_CHG_VOLTAGE 0x15 /* ChargeVoltage */
#define SBS_GAUGE_CMD_FLAGS 0x16 /* BatteryStatus */
#define SBS_GAUGE_CMD_CYCLE_COUNT 0x17 /* CycleCount */
#define SBS_GAUGE_CMD_NOM_CAPACITY 0x18 /* DesignCapacity */
#define SBS_GAUGE_CMD_DESIGN_VOLTAGE 0x19 /* DesignVoltage */
#define SBS_GAUGE_CMD_SPECS_INFO 0x1A /* SpecificationInfo */
#define SBS_GAUGE_CMD_MANUFACTURER_DATE 0x1B /* ManufacturerDate */
#define SBS_GAUGE_CMD_SN 0x1C /* SerialNumber */
#define SBS_GAUGE_CMD_MANUFACTURER_NAME 0x20 /* ManufacturerName */
#define SBS_GAUGE_CMD_DEVICE_NAME 0x21 /* DeviceName */
#define SBS_GAUGE_CMD_DEVICE_CHEM 0x22 /* DeviceChemistry */
#define SBS_GAUGE_CMD_MANUFACTURER_DATA 0x23 /* ManufacturerData */

#define SBS_GAUGE_DELAY 1000

struct sbs_gauge_config {
struct i2c_dt_spec i2c;
};

#endif
5 changes: 5 additions & 0 deletions dts/bindings/fuel-gauge/sbs,sbs-gauge-new-api.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
description: SBS 1.1 compliant fuel gauge (http://www.sbs-forum.org/specs)

compatible: "sbs,sbs-gauge-new-api"

include: [i2c-device.yaml, fuel-gauge.yaml]
5 changes: 4 additions & 1 deletion subsys/emul/i2c/emul_sbs_gauge.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
* Emulator for SBS 1.1 compliant smart battery fuel gauge.
*/

#ifdef CONFIG_FUEL_GAUGE
#define DT_DRV_COMPAT sbs_sbs_gauge_new_api
#else
#define DT_DRV_COMPAT sbs_sbs_gauge
#endif /* CONFIG_FUEL_GAUGE */

#define LOG_LEVEL CONFIG_SENSOR_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(sbs_sbs_gauge);

Expand Down
8 changes: 8 additions & 0 deletions tests/drivers/fuel_gauge/sbs_gauge/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(device)

FILE(GLOB app_sources src/test_sbs_gauge.c)
target_sources(app PRIVATE ${app_sources})
7 changes: 7 additions & 0 deletions tests/drivers/fuel_gauge/sbs_gauge/boards/native_posix.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copyright (c) 2022 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0

CONFIG_EMUL=y
CONFIG_EMUL_SBS_GAUGE=y
CONFIG_I2C=y
CONFIG_I2C_EMUL=y
14 changes: 14 additions & 0 deletions tests/drivers/fuel_gauge/sbs_gauge/boards/native_posix.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright (c) 2022 Leica Geosystems AG
*
* SPDX-License-Identifier: Apache-2.0
*/

&i2c0 {
clock-frequency = <I2C_BITRATE_STANDARD>;
smartbattery: sbs_gauge@b {
compatible = "sbs,sbs-gauge-new-api";
reg = <0x0B>;
status = "okay";
};
};
7 changes: 7 additions & 0 deletions tests/drivers/fuel_gauge/sbs_gauge/boards/qemu_cortex_a9.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copyright (c) 2022 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0

CONFIG_EMUL=y
CONFIG_EMUL_SBS_GAUGE=y
CONFIG_I2C=y
CONFIG_I2C_EMUL=y
29 changes: 29 additions & 0 deletions tests/drivers/fuel_gauge/sbs_gauge/boards/qemu_cortex_a9.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2022 Google LLC
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/dt-bindings/i2c/i2c.h>

/ {
/* qemu_cortex_a9 board isn't configured with an I2C node */
fake_i2c_bus: i2c@100 {
status = "okay";
compatible = "zephyr,i2c-emul-controller";
clock-frequency = <I2C_BITRATE_STANDARD>;
#address-cells = <1>;
#size-cells = <0>;
reg = <0x100 4>;
};
};

&fake_i2c_bus {
clock-frequency = <I2C_BITRATE_STANDARD>;
compatible = "zephyr,i2c-emul-controller";
smartbattery0: smartbattery@b {
compatible = "sbs,sbs-gauge-new-api";
reg = <0x0B>;
status = "okay";
};
};
7 changes: 7 additions & 0 deletions tests/drivers/fuel_gauge/sbs_gauge/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CONFIG_ZTEST=y
CONFIG_ZTEST_NEW_API=y
CONFIG_I2C=y
CONFIG_TEST_USERSPACE=y
CONFIG_LOG=y

CONFIG_FUEL_GAUGE=y
49 changes: 49 additions & 0 deletions tests/drivers/fuel_gauge/sbs_gauge/src/test_sbs_gauge.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2022 Google LLC
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/device.h>
#include <zephyr/drivers/fuel_gauge.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/ztest.h>
#include <zephyr/ztest_assert.h>

struct sbs_gauge_new_api_fixture {
const struct device *dev;
const struct battery_driver_api *api;
};

static void *sbs_gauge_new_api_setup(void)
{

static struct sbs_gauge_new_api_fixture fixture;
const struct device *dev = DEVICE_DT_GET_ANY(sbs_sbs_gauge_new_api);
const struct battery_driver_api *api = dev->api;

fixture.dev = dev;
fixture.api = api;

zassert_true(device_is_ready(fixture.dev), "Fuel Gauge not found");
zassert_not_null(api);

return &fixture;
}

ZTEST_F(sbs_gauge_new_api, test_implemented_apis)
{
zassert_not_equal(NULL, fixture->api->get_property);
}

ZTEST_F(sbs_gauge_new_api, test_voltage_read)
{
struct fuel_gauge_get_property prop = {
.property_type = FUEL_GAUGE_VOLTAGE,
};
zassert_ok(fixture->api->get_property(fixture->dev, &prop, 1));
}

ZTEST_SUITE(sbs_gauge_new_api, NULL, sbs_gauge_new_api_setup, NULL, NULL, NULL);
Loading

0 comments on commit 28b8123

Please sign in to comment.