From 28b8123887eab36d4370daa70ab27a563ec59f8f Mon Sep 17 00:00:00 2001 From: Aaron Massey Date: Mon, 14 Nov 2022 14:26:03 -0700 Subject: [PATCH] fuel_gauge: Sample sbs gauge driver with tests 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 --- drivers/CMakeLists.txt | 1 + drivers/Kconfig | 2 + drivers/fuel_gauge/CMakeLists.txt | 3 + drivers/fuel_gauge/Kconfig | 18 +++ drivers/fuel_gauge/sbs_gauge/CMakeLists.txt | 3 + drivers/fuel_gauge/sbs_gauge/Kconfig | 10 ++ drivers/fuel_gauge/sbs_gauge/sbs_gauge.c | 138 ++++++++++++++++++ drivers/fuel_gauge/sbs_gauge/sbs_gauge.h | 54 +++++++ .../fuel-gauge/sbs,sbs-gauge-new-api.yaml | 5 + subsys/emul/i2c/emul_sbs_gauge.c | 5 +- .../fuel_gauge/sbs_gauge/CMakeLists.txt | 8 + .../sbs_gauge/boards/native_posix.conf | 7 + .../sbs_gauge/boards/native_posix.overlay | 14 ++ .../sbs_gauge/boards/qemu_cortex_a9.conf | 7 + .../sbs_gauge/boards/qemu_cortex_a9.overlay | 29 ++++ tests/drivers/fuel_gauge/sbs_gauge/prj.conf | 7 + .../fuel_gauge/sbs_gauge/src/test_sbs_gauge.c | 49 +++++++ .../fuel_gauge/sbs_gauge/testcase.yaml | 11 ++ 18 files changed, 370 insertions(+), 1 deletion(-) create mode 100644 drivers/fuel_gauge/CMakeLists.txt create mode 100644 drivers/fuel_gauge/Kconfig create mode 100644 drivers/fuel_gauge/sbs_gauge/CMakeLists.txt create mode 100644 drivers/fuel_gauge/sbs_gauge/Kconfig create mode 100644 drivers/fuel_gauge/sbs_gauge/sbs_gauge.c create mode 100644 drivers/fuel_gauge/sbs_gauge/sbs_gauge.h create mode 100644 dts/bindings/fuel-gauge/sbs,sbs-gauge-new-api.yaml create mode 100644 tests/drivers/fuel_gauge/sbs_gauge/CMakeLists.txt create mode 100644 tests/drivers/fuel_gauge/sbs_gauge/boards/native_posix.conf create mode 100644 tests/drivers/fuel_gauge/sbs_gauge/boards/native_posix.overlay create mode 100644 tests/drivers/fuel_gauge/sbs_gauge/boards/qemu_cortex_a9.conf create mode 100644 tests/drivers/fuel_gauge/sbs_gauge/boards/qemu_cortex_a9.overlay create mode 100644 tests/drivers/fuel_gauge/sbs_gauge/prj.conf create mode 100644 tests/drivers/fuel_gauge/sbs_gauge/src/test_sbs_gauge.c create mode 100644 tests/drivers/fuel_gauge/sbs_gauge/testcase.yaml diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index 16b73d07f75..b5c19740c30 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -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) diff --git a/drivers/Kconfig b/drivers/Kconfig index 992ff33d9b2..23fecff92d1 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -149,4 +149,6 @@ source "drivers/coredump/Kconfig" source "drivers/xen/Kconfig" +source "drivers/fuel_gauge/Kconfig" + endmenu diff --git a/drivers/fuel_gauge/CMakeLists.txt b/drivers/fuel_gauge/CMakeLists.txt new file mode 100644 index 00000000000..c68e640c34d --- /dev/null +++ b/drivers/fuel_gauge/CMakeLists.txt @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: Apache-2.0 + +add_subdirectory_ifdef(CONFIG_SBS_GAUGE_NEW_API sbs_gauge) diff --git a/drivers/fuel_gauge/Kconfig b/drivers/fuel_gauge/Kconfig new file mode 100644 index 00000000000..e85af9f79f2 --- /dev/null +++ b/drivers/fuel_gauge/Kconfig @@ -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 diff --git a/drivers/fuel_gauge/sbs_gauge/CMakeLists.txt b/drivers/fuel_gauge/sbs_gauge/CMakeLists.txt new file mode 100644 index 00000000000..5fdf28d3bef --- /dev/null +++ b/drivers/fuel_gauge/sbs_gauge/CMakeLists.txt @@ -0,0 +1,3 @@ +zephyr_library() + +zephyr_library_sources(sbs_gauge.c) diff --git a/drivers/fuel_gauge/sbs_gauge/Kconfig b/drivers/fuel_gauge/sbs_gauge/Kconfig new file mode 100644 index 00000000000..8c3a7e281f3 --- /dev/null +++ b/drivers/fuel_gauge/sbs_gauge/Kconfig @@ -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. diff --git a/drivers/fuel_gauge/sbs_gauge/sbs_gauge.c b/drivers/fuel_gauge/sbs_gauge/sbs_gauge.c new file mode 100644 index 00000000000..023f65d0076 --- /dev/null +++ b/drivers/fuel_gauge/sbs_gauge/sbs_gauge.c @@ -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 +#include +#include +#include + +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) diff --git a/drivers/fuel_gauge/sbs_gauge/sbs_gauge.h b/drivers/fuel_gauge/sbs_gauge/sbs_gauge.h new file mode 100644 index 00000000000..7a465593667 --- /dev/null +++ b/drivers/fuel_gauge/sbs_gauge/sbs_gauge.h @@ -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 +#include + +/** 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 diff --git a/dts/bindings/fuel-gauge/sbs,sbs-gauge-new-api.yaml b/dts/bindings/fuel-gauge/sbs,sbs-gauge-new-api.yaml new file mode 100644 index 00000000000..4871472752f --- /dev/null +++ b/dts/bindings/fuel-gauge/sbs,sbs-gauge-new-api.yaml @@ -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] diff --git a/subsys/emul/i2c/emul_sbs_gauge.c b/subsys/emul/i2c/emul_sbs_gauge.c index 9b1163026c4..7d1a3163588 100644 --- a/subsys/emul/i2c/emul_sbs_gauge.c +++ b/subsys/emul/i2c/emul_sbs_gauge.c @@ -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 LOG_MODULE_REGISTER(sbs_sbs_gauge); diff --git a/tests/drivers/fuel_gauge/sbs_gauge/CMakeLists.txt b/tests/drivers/fuel_gauge/sbs_gauge/CMakeLists.txt new file mode 100644 index 00000000000..9aaf0d0cd93 --- /dev/null +++ b/tests/drivers/fuel_gauge/sbs_gauge/CMakeLists.txt @@ -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}) diff --git a/tests/drivers/fuel_gauge/sbs_gauge/boards/native_posix.conf b/tests/drivers/fuel_gauge/sbs_gauge/boards/native_posix.conf new file mode 100644 index 00000000000..065b1009a4a --- /dev/null +++ b/tests/drivers/fuel_gauge/sbs_gauge/boards/native_posix.conf @@ -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 diff --git a/tests/drivers/fuel_gauge/sbs_gauge/boards/native_posix.overlay b/tests/drivers/fuel_gauge/sbs_gauge/boards/native_posix.overlay new file mode 100644 index 00000000000..1e386873b16 --- /dev/null +++ b/tests/drivers/fuel_gauge/sbs_gauge/boards/native_posix.overlay @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2022 Leica Geosystems AG + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&i2c0 { + clock-frequency = ; + smartbattery: sbs_gauge@b { + compatible = "sbs,sbs-gauge-new-api"; + reg = <0x0B>; + status = "okay"; + }; +}; diff --git a/tests/drivers/fuel_gauge/sbs_gauge/boards/qemu_cortex_a9.conf b/tests/drivers/fuel_gauge/sbs_gauge/boards/qemu_cortex_a9.conf new file mode 100644 index 00000000000..065b1009a4a --- /dev/null +++ b/tests/drivers/fuel_gauge/sbs_gauge/boards/qemu_cortex_a9.conf @@ -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 diff --git a/tests/drivers/fuel_gauge/sbs_gauge/boards/qemu_cortex_a9.overlay b/tests/drivers/fuel_gauge/sbs_gauge/boards/qemu_cortex_a9.overlay new file mode 100644 index 00000000000..4ddad45c2b2 --- /dev/null +++ b/tests/drivers/fuel_gauge/sbs_gauge/boards/qemu_cortex_a9.overlay @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022 Google LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + /* 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 = ; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x100 4>; + }; +}; + +&fake_i2c_bus { + clock-frequency = ; + compatible = "zephyr,i2c-emul-controller"; + smartbattery0: smartbattery@b { + compatible = "sbs,sbs-gauge-new-api"; + reg = <0x0B>; + status = "okay"; + }; +}; diff --git a/tests/drivers/fuel_gauge/sbs_gauge/prj.conf b/tests/drivers/fuel_gauge/sbs_gauge/prj.conf new file mode 100644 index 00000000000..23ceb0b3cdd --- /dev/null +++ b/tests/drivers/fuel_gauge/sbs_gauge/prj.conf @@ -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 diff --git a/tests/drivers/fuel_gauge/sbs_gauge/src/test_sbs_gauge.c b/tests/drivers/fuel_gauge/sbs_gauge/src/test_sbs_gauge.c new file mode 100644 index 00000000000..60f50260023 --- /dev/null +++ b/tests/drivers/fuel_gauge/sbs_gauge/src/test_sbs_gauge.c @@ -0,0 +1,49 @@ +/* + * Copyright 2022 Google LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +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); diff --git a/tests/drivers/fuel_gauge/sbs_gauge/testcase.yaml b/tests/drivers/fuel_gauge/sbs_gauge/testcase.yaml new file mode 100644 index 00000000000..0d11875ce79 --- /dev/null +++ b/tests/drivers/fuel_gauge/sbs_gauge/testcase.yaml @@ -0,0 +1,11 @@ +tests: + # section.subsection + drivers.sbs_gauge_new_api: + tags: test_framework + filter: dt_compat_enabled("sbs,sbs-gauge-new-api") + platform_allow: + native_posix + qemu_cortex_a9 + nucleo_f070rb + integration_platforms: + - nucleo_f070rb