From 387f3c2092d736b80f913128b2ff09910659f4ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20Kr=C3=BCger?= Date: Mon, 19 Jun 2023 16:33:13 +0200 Subject: [PATCH] drivers: fuelgauge: Add TI BQ27z746 driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add driver for the Texas Instruments BQ27z746 fuel gauge Signed-off-by: Marcel Krüger --- drivers/fuel_gauge/CMakeLists.txt | 1 + drivers/fuel_gauge/Kconfig | 2 +- drivers/fuel_gauge/bq27z746/CMakeLists.txt | 6 + drivers/fuel_gauge/bq27z746/Kconfig | 21 ++ drivers/fuel_gauge/bq27z746/bq27z746.c | 324 ++++++++++++++++ drivers/fuel_gauge/bq27z746/bq27z746.h | 142 +++++++ drivers/fuel_gauge/bq27z746/emul_bq27z746.c | 352 ++++++++++++++++++ dts/bindings/fuel-gauge/ti,bq27z746.yaml | 13 + .../fuel_gauge/bq27z746/CMakeLists.txt | 8 + .../bq27z746/boards/native_posix.conf | 3 + .../bq27z746/boards/native_posix.overlay | 11 + tests/drivers/fuel_gauge/bq27z746/prj.conf | 7 + .../fuel_gauge/bq27z746/src/test_bq27z746.c | 246 ++++++++++++ .../drivers/fuel_gauge/bq27z746/testcase.yaml | 5 + 14 files changed, 1140 insertions(+), 1 deletion(-) create mode 100644 drivers/fuel_gauge/bq27z746/CMakeLists.txt create mode 100644 drivers/fuel_gauge/bq27z746/Kconfig create mode 100644 drivers/fuel_gauge/bq27z746/bq27z746.c create mode 100644 drivers/fuel_gauge/bq27z746/bq27z746.h create mode 100644 drivers/fuel_gauge/bq27z746/emul_bq27z746.c create mode 100644 dts/bindings/fuel-gauge/ti,bq27z746.yaml create mode 100644 tests/drivers/fuel_gauge/bq27z746/CMakeLists.txt create mode 100644 tests/drivers/fuel_gauge/bq27z746/boards/native_posix.conf create mode 100644 tests/drivers/fuel_gauge/bq27z746/boards/native_posix.overlay create mode 100644 tests/drivers/fuel_gauge/bq27z746/prj.conf create mode 100644 tests/drivers/fuel_gauge/bq27z746/src/test_bq27z746.c create mode 100644 tests/drivers/fuel_gauge/bq27z746/testcase.yaml diff --git a/drivers/fuel_gauge/CMakeLists.txt b/drivers/fuel_gauge/CMakeLists.txt index 6a95c89ea46..c48e2ead003 100644 --- a/drivers/fuel_gauge/CMakeLists.txt +++ b/drivers/fuel_gauge/CMakeLists.txt @@ -4,6 +4,7 @@ zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/fuel_gauge.h) add_subdirectory_ifdef(CONFIG_SBS_GAUGE_NEW_API sbs_gauge) add_subdirectory_ifdef(CONFIG_MAX17048 max17048) +add_subdirectory_ifdef(CONFIG_BQ27Z746 bq27z746) zephyr_library_sources_ifdef(CONFIG_USERSPACE fuel_gauge_syscall_handlers.c) diff --git a/drivers/fuel_gauge/Kconfig b/drivers/fuel_gauge/Kconfig index 929dd060490..616ab3885d2 100644 --- a/drivers/fuel_gauge/Kconfig +++ b/drivers/fuel_gauge/Kconfig @@ -20,7 +20,7 @@ config FUEL_GAUGE_INIT_PRIORITY Battery fuel gauge initialization priority. source "drivers/fuel_gauge/max17048/Kconfig" - source "drivers/fuel_gauge/sbs_gauge/Kconfig" +source "drivers/fuel_gauge/bq27z746/Kconfig" endif # FUEL_GAUGE diff --git a/drivers/fuel_gauge/bq27z746/CMakeLists.txt b/drivers/fuel_gauge/bq27z746/CMakeLists.txt new file mode 100644 index 00000000000..ff6a6ff0343 --- /dev/null +++ b/drivers/fuel_gauge/bq27z746/CMakeLists.txt @@ -0,0 +1,6 @@ +zephyr_library() + +zephyr_library_sources(bq27z746.c) + +zephyr_include_directories_ifdef(CONFIG_EMUL_BQ27Z746 .) +zephyr_library_sources_ifdef(CONFIG_EMUL_BQ27Z746 ./emul_bq27z746.c) diff --git a/drivers/fuel_gauge/bq27z746/Kconfig b/drivers/fuel_gauge/bq27z746/Kconfig new file mode 100644 index 00000000000..26f3d2612d8 --- /dev/null +++ b/drivers/fuel_gauge/bq27z746/Kconfig @@ -0,0 +1,21 @@ +# Copyright (c) 2023, ithinx GmbH +# Copyright (c) 2023, Tonies GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +config BQ27Z746 + bool "BQ27Z746 Fuel Gauge" + default y + depends on DT_HAS_TI_BQ27Z746_ENABLED + select I2C + help + Enable I2C-based driver for BQ27Z746 Fuel Gauge. + +config EMUL_BQ27Z746 + bool "Emulate an BQ27Z746 fuel gague" + default y + depends on EMUL + depends on BQ27Z746 + help + It provides readings which follow a simple sequence, thus allowing + test code to check that things are working as expected. diff --git a/drivers/fuel_gauge/bq27z746/bq27z746.c b/drivers/fuel_gauge/bq27z746/bq27z746.c new file mode 100644 index 00000000000..802de27bad9 --- /dev/null +++ b/drivers/fuel_gauge/bq27z746/bq27z746.c @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2023, ithinx GmbH + * Copyright (c) 2023, Tonies GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ti_bq27z746 + +#include "bq27z746.h" + +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(BQ27Z746); + +#define BQ27Z746_MAC_DATA_LEN 32 +#define BQ27Z746_MAC_OVERHEAD_LEN 4 /* 2 cmd bytes, 1 length byte, 1 checksum byte */ +#define BQ27Z746_MAC_COMPLETE_LEN (BQ27Z746_MAC_DATA_LEN + BQ27Z746_MAC_OVERHEAD_LEN) + +static int bq27z746_read16(const struct device *dev, uint8_t reg, uint16_t *value) +{ + uint8_t i2c_data[2]; + const struct bq27z746_config *cfg = dev->config; + const int status = i2c_burst_read_dt(&cfg->i2c, reg, i2c_data, sizeof(i2c_data)); + + if (status < 0) { + LOG_ERR("Unable to read register"); + return status; + } + *value = sys_get_le16(i2c_data); + + return 0; +} + +static int bq27z746_write16(const struct device *dev, uint8_t reg, uint16_t value) +{ + uint8_t buf[3]; + const struct bq27z746_config *cfg = dev->config; + + buf[0] = reg; + sys_put_le16(value, &buf[1]); + + return i2c_write_dt(&cfg->i2c, buf, sizeof(buf)); +} + +static int bq27z746_read_mac(const struct device *dev, uint16_t cmd, uint8_t *data, int len) +{ + if (len > BQ27Z746_MAC_DATA_LEN) { + return -EINVAL; + } + + uint8_t buf[BQ27Z746_MAC_COMPLETE_LEN]; + const struct bq27z746_config *cfg = dev->config; + + /* Instead of MAC, ALTMAC is used as reccommended in the datasheet */ + int ret = bq27z746_write16(dev, BQ27Z746_ALTMANUFACTURERACCESS, cmd); + + if (ret != 0) { + return ret; + } + + /* + * The data read from BQ27Z746_ALTMANUFACTURERACCESS is: + * 0..1: The command (for verification) + * 2..33: The data + * 34: Checksum calculated as (uint8_t)(0xFF - (sum of all command and data bytes)) + * 35: Length including command, checksum and length (e.g. data length + 4) + */ + + ret = i2c_burst_read_dt(&cfg->i2c, BQ27Z746_ALTMANUFACTURERACCESS, buf, + BQ27Z746_MAC_COMPLETE_LEN); + if (ret != 0) { + return ret; + } + + /* The first two bytes read is the command and is used for verification */ + const uint16_t read_cmd = sys_get_le16(buf); + + if (read_cmd != cmd) { + LOG_ERR("Read command 0x%x != written command 0x%x", read_cmd, cmd); + return -EIO; + } + + const uint8_t checksum_actual = buf[34]; + uint8_t sum = 0; /* Intentionally 8 bit wide and overflowing */ + + for (int i = 0; i < BQ27Z746_MAC_COMPLETE_LEN - 2; i++) { + sum += buf[i]; + } + + const uint8_t checksum_expected = 0xFF - sum; + + if (checksum_expected != checksum_actual) { + LOG_ERR("Checksum mismatch"); + return -EIO; + } + + /* First byte of the user buffer is the length */ + data[0] = buf[35] - BQ27Z746_MAC_OVERHEAD_LEN; + + /* Copy only the data to the user buffer (= skipping the first two command bytes) */ + memcpy(&data[1], &buf[2], len); + + return ret; +} + +static int bq27z746_get_prop(const struct device *dev, struct fuel_gauge_get_property *prop) +{ + int rc = 0; + uint16_t val = 0; + + /* + * Possibly negative values must be cast from uint16 to int16 first to + * then correctly end up in the wider datatypes of `prop`. + */ + + switch (prop->property_type) { + case FUEL_GAUGE_AVG_CURRENT: + rc = bq27z746_read16(dev, BQ27Z746_AVERAGECURRENT, &val); + prop->value.avg_current = (int16_t)val * 1000; + break; + case FUEL_GAUGE_CYCLE_COUNT: + rc = bq27z746_read16(dev, BQ27Z746_CYCLECOUNT, &val); + prop->value.cycle_count = val * 100; + break; + case FUEL_GAUGE_CURRENT: + rc = bq27z746_read16(dev, BQ27Z746_CURRENT, &val); + prop->value.current = (int16_t)val * 1000; + break; + case FUEL_GAUGE_FULL_CHARGE_CAPACITY: + rc = bq27z746_read16(dev, BQ27Z746_FULLCHARGECAPACITY, &val); + prop->value.full_charge_capacity = val * 1000; + break; + case FUEL_GAUGE_REMAINING_CAPACITY: + rc = bq27z746_read16(dev, BQ27Z746_REMAININGCAPACITY, &val); + prop->value.remaining_capacity = val * 1000; + break; + case FUEL_GAUGE_RUNTIME_TO_EMPTY: + rc = bq27z746_read16(dev, BQ27Z746_AVERAGETIMETOEMPTY, &val); + prop->value.runtime_to_empty = val; + break; + case FUEL_GAUGE_RUNTIME_TO_FULL: + rc = bq27z746_read16(dev, BQ27Z746_AVERAGETIMETOFULL, &val); + prop->value.runtime_to_full = val; + break; + case FUEL_GAUGE_SBS_MFR_ACCESS: + rc = bq27z746_read16(dev, BQ27Z746_MANUFACTURERACCESS, &val); + prop->value.sbs_mfr_access_word = val; + break; + case FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE: + rc = bq27z746_read16(dev, BQ27Z746_RELATIVESTATEOFCHARGE, &val); + prop->value.relative_state_of_charge = val; + break; + case FUEL_GAUGE_TEMPERATURE: + rc = bq27z746_read16(dev, BQ27Z746_TEMPERATURE, &val); + prop->value.temperature = val; + break; + case FUEL_GAUGE_VOLTAGE: + rc = bq27z746_read16(dev, BQ27Z746_VOLTAGE, &val); + prop->value.voltage = val * 1000; + break; + case FUEL_GAUGE_SBS_ATRATE: + rc = bq27z746_read16(dev, BQ27Z746_ATRATE, &val); + prop->value.sbs_at_rate = (int16_t)val; + break; + case FUEL_GAUGE_SBS_ATRATE_TIME_TO_EMPTY: + rc = bq27z746_read16(dev, BQ27Z746_ATRATETIMETOEMPTY, &val); + prop->value.sbs_at_rate_time_to_empty = val; + break; + case FUEL_GAUGE_CHARGE_VOLTAGE: + rc = bq27z746_read16(dev, BQ27Z746_CHARGINGVOLTAGE, &val); + prop->value.chg_voltage = val; + break; + case FUEL_GAUGE_CHARGE_CURRENT: + rc = bq27z746_read16(dev, BQ27Z746_CHARGINGCURRENT, &val); + prop->value.chg_current = val; + break; + case FUEL_GAUGE_STATUS: + rc = bq27z746_read16(dev, BQ27Z746_BATTERYSTATUS, &val); + prop->value.fg_status = val; + break; + case FUEL_GAUGE_DESIGN_CAPACITY: + rc = bq27z746_read16(dev, BQ27Z746_DESIGNCAPACITY, &val); + prop->value.design_cap = val; + break; + default: + rc = -ENOTSUP; + } + + prop->status = rc; + + return rc; +} + +static int bq27z746_get_buffer_prop(const struct device *dev, + struct fuel_gauge_get_buffer_property *prop, void *dst, + size_t dst_len) +{ + int rc = 0; + + switch (prop->property_type) { + case FUEL_GAUGE_MANUFACTURER_NAME: + if (dst_len == sizeof(struct sbs_gauge_manufacturer_name)) { + rc = bq27z746_read_mac(dev, BQ27Z746_MAC_CMD_MANUFACTURER_NAME, + (uint8_t *)dst, dst_len - 1); + } else { + rc = -EINVAL; + } + break; + case FUEL_GAUGE_DEVICE_NAME: + if (dst_len == sizeof(struct sbs_gauge_device_name)) { + rc = bq27z746_read_mac(dev, BQ27Z746_MAC_CMD_DEVICE_NAME, (uint8_t *)dst, + dst_len - 1); + } else { + rc = -EINVAL; + } + break; + case FUEL_GAUGE_DEVICE_CHEMISTRY: + if (dst_len == sizeof(struct sbs_gauge_device_chemistry)) { + rc = bq27z746_read_mac(dev, BQ27Z746_MAC_CMD_DEVICE_CHEM, (uint8_t *)dst, + dst_len - 1); + } else { + rc = -EINVAL; + } + break; + default: + rc = -ENOTSUP; + } + + prop->status = rc; + return rc; +} + +static int bq27z746_set_prop(const struct device *dev, struct fuel_gauge_set_property *prop) +{ + int rc = 0; + uint16_t val = 0; + + switch (prop->property_type) { + case FUEL_GAUGE_SBS_MFR_ACCESS: + rc = bq27z746_write16(dev, BQ27Z746_MANUFACTURERACCESS, + prop->value.sbs_mfr_access_word); + prop->value.sbs_mfr_access_word = val; + break; + case FUEL_GAUGE_SBS_ATRATE: + rc = bq27z746_write16(dev, BQ27Z746_ATRATE, prop->value.sbs_at_rate); + prop->value.sbs_at_rate = val; + break; + default: + rc = -ENOTSUP; + } + + prop->status = rc; + + return rc; +} + +static int bq27z746_get_props(const struct device *dev, struct fuel_gauge_get_property *props, + size_t len) +{ + int err_count = 0; + + for (int i = 0; i < len; i++) { + int ret = bq27z746_get_prop(dev, props + i); + + err_count += ret ? 1 : 0; + } + + err_count = (err_count == len) ? -1 : err_count; + + return err_count; +} + +static int bq27z746_set_props(const struct device *dev, struct fuel_gauge_set_property *props, + size_t len) +{ + int err_count = 0; + + for (int i = 0; i < len; i++) { + int ret = bq27z746_set_prop(dev, props + i); + + err_count += ret ? 1 : 0; + } + + err_count = (err_count == len) ? -1 : err_count; + + return err_count; +} + +static int bq27z746_init(const struct device *dev) +{ + const struct bq27z746_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 fuel_gauge_driver_api bq27z746_driver_api = { + .get_property = &bq27z746_get_props, + .set_property = &bq27z746_set_props, + .get_buffer_property = &bq27z746_get_buffer_prop, +}; + +#define BQ27Z746_INIT(index) \ + \ + static const struct bq27z746_config bq27z746_config_##index = { \ + .i2c = I2C_DT_SPEC_INST_GET(index), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(index, &bq27z746_init, NULL, NULL, &bq27z746_config_##index, \ + POST_KERNEL, CONFIG_FUEL_GAUGE_INIT_PRIORITY, &bq27z746_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(BQ27Z746_INIT) diff --git a/drivers/fuel_gauge/bq27z746/bq27z746.h b/drivers/fuel_gauge/bq27z746/bq27z746.h new file mode 100644 index 00000000000..6d9f821b90d --- /dev/null +++ b/drivers/fuel_gauge/bq27z746/bq27z746.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2023, ithinx GmbH + * Copyright (c) 2023, Tonies GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_FUELGAUGE_BQ27Z746_GAUGE_H_ +#define ZEPHYR_DRIVERS_FUELGAUGE_BQ27Z746_GAUGE_H_ + +#include + +/* Registers */ +#define BQ27Z746_MANUFACTURERACCESS 0x00 /* R/W */ +#define BQ27Z746_ATRATE 0x02 /* R/W, Unit: mA, Range: -32768..32767 */ +#define BQ27Z746_ATRATETIMETOEMPTY 0x04 /* R/O, Unit: minutes, Range: 0..65535 */ +#define BQ27Z746_TEMPERATURE 0x06 /* R/O, Unit: 0.1 K, Range: 0..32767 */ +#define BQ27Z746_VOLTAGE 0x08 /* R/O, Unit: mV, Range: 0..32767 */ +#define BQ27Z746_BATTERYSTATUS 0x0A /* R/O, Unit: status bits */ +#define BQ27Z746_CURRENT 0x0C /* R/O, Unit: mA, Range: -32768..32767 */ +#define BQ27Z746_REMAININGCAPACITY 0x10 /* R/O, Unit: mAh, Range: 0..32767 */ +#define BQ27Z746_FULLCHARGECAPACITY 0x12 /* R/O, Unit: mAh, Range: 0..32767 */ +#define BQ27Z746_AVERAGECURRENT 0x14 /* R/O, Unit: mA, Range: -32768..32767 */ +#define BQ27Z746_AVERAGETIMETOEMPTY 0x16 /* R/O, Unit: minutes, Range: 0..65535 */ +#define BQ27Z746_AVERAGETIMETOFULL 0x18 /* R/O, Unit: minutes, Range: 0..65535 */ +#define BQ27Z746_MAXLOADCURRENT 0x1E /* R/O, Unit: mA, Range: 0..65535 */ +#define BQ27Z746_MAXLOADTIMETOEMPTY 0x20 /* R/O, Unit: minutes, Range: 0..65535 */ +#define BQ27Z746_AVERAGEPOWER 0x22 /* R/O, Unit: mW, Range: -32768..32767 */ +#define BQ27Z746_BTPDISCHARGESET 0x24 /* Datasheet unclear */ +#define BQ27Z746_BTPCHARGESET 0x26 /* Datasheet unclear */ +#define BQ27Z746_INTERNALTEMPERATURE 0x28 /* R/O, Unit: 0.1 K, Range: 0..32767 */ +#define BQ27Z746_CYCLECOUNT 0x2A /* R/O, Unit: none, Range: 0..65535 */ +#define BQ27Z746_RELATIVESTATEOFCHARGE 0x2C /* R/O, Unit: percent, Range: 0..100 */ +#define BQ27Z746_STATEOFHEALTH 0x2E /* R/O, Unit: percent, Range: 0..100 */ +#define BQ27Z746_CHARGINGVOLTAGE 0x30 /* R/O, Unit: mV, Range: 0..32767 */ +#define BQ27Z746_CHARGINGCURRENT 0x32 /* R/O, Unit: mA, Range: 0..32767 */ +#define BQ27Z746_TERMINATEVOLTAGE 0x34 /* R/W, Unit: mC, Range: 0..32767 */ +#define BQ27Z746_TIMESTAMPUPPER 0x36 /* R/O, Unit: seconds, Range: 0..65535 */ +#define BQ27Z746_TIMESTAMPLOWER 0x38 /* R/O, Unit: seconds, Range: 0..65535 */ +#define BQ27Z746_QMAXCYCLES 0x3A /* R/O, Unit: none, Range: 0..65535 */ +#define BQ27Z746_DESIGNCAPACITY \ + 0x3C /* R/O (sealed), R/W (unsealed or factory access), Unit: mAh, Range: 0..32767 */ +#define BQ27Z746_ALTMANUFACTURERACCESS 0x3E /* R/W */ +#define BQ27Z746_MACDATA 0x40 /* R/O, MAC data */ +#define BQ27Z746_MACDATASUM 0x60 /* R/O, Checksum over MAC command and data */ +#define BQ27Z746_MACDATALEN 0x61 /* R/O, Length of the MAC data */ +#define BQ27Z746_VOLTHISETTHRESHOLD 0x62 /* R/W, Unit: mV, Range: 0..5000 */ +#define BQ27Z746_VOLTHICLEARTHRESHOLD 0x64 /* R/W, Unit: mV, Range: 0..5000 */ +#define BQ27Z746_VOLTLOSETTHRESHOLD 0x66 /* R/W, Unit: mV, Range: 0..5000 */ +#define BQ27Z746_VOLTLOCLEARTHRESHOLD 0x68 /* R/W, Unit: mV, Range: 0..5000 */ +#define BQ27Z746_TEMPHISETTHRESHOLD 0x6A /* R/W, Unit: degree celsius, Range: -128..127 */ +#define BQ27Z746_TEMPHICLEARTHRESHOLD 0x6B /* R/W, Unit: degree celsius, Range: -128..127 */ +#define BQ27Z746_TEMPLOSETTHRESHOLD 0x6C /* R/W, Unit: degree celsius, Range: -128..127 */ +#define BQ27Z746_TEMPLOCLEARTHRESHOLD 0x6D /* R/W, Unit: degree celsius, Range: -128..127 */ +#define BQ27Z746_INTERRUPTSTATUS 0x6E /* R/O, Unit: status bits */ +#define BQ27Z746_SOCDELTASETTHRESHOLD 0x6F /* R/W, Unit: percent, Range: 0..100 */ + +/* MAC commands */ +#define BQ27Z746_MAC_CMD_DEVICETYPE 0x0001 +#define BQ27Z746_MAC_CMD_FIRMWAREVERSION 0x0002 +#define BQ27Z746_MAC_CMD_HARDWAREVERSION 0x0003 +#define BQ27Z746_MAC_CMD_IFCHECKSUM 0x0004 +#define BQ27Z746_MAC_CMD_STATICDFSIGNATURE 0x0005 +#define BQ27Z746_MAC_CMD_CHEMID 0x0006 +#define BQ27Z746_MAC_CMD_PREV_MACWRITE 0x0007 +#define BQ27Z746_MAC_CMD_STATICCHEMDFSIGNATURE 0x0008 +#define BQ27Z746_MAC_CMD_ALLDFSIGNATURE 0x0009 +#define BQ27Z746_MAC_CMD_SHELFENABLE 0x000B +#define BQ27Z746_MAC_CMD_SHELFDISABLE 0x000C +#define BQ27Z746_MAC_CMD_SHUTDOWNMODE 0x0010 +#define BQ27Z746_MAC_CMD_RESET1 0x0012 +#define BQ27Z746_MAC_CMD_SHIPMODEENABLE 0x0015 +#define BQ27Z746_MAC_CMD_SHIPMODEDISABLE 0x0016 +#define BQ27Z746_MAC_CMD_QMAX_DAY 0x0017 +#define BQ27Z746_MAC_CMD_CHARGEFETTOGGLE 0x001F +#define BQ27Z746_MAC_CMD_DISCHARGEFETTOGGLE 0x0020 +#define BQ27Z746_MAC_CMD_GAUGING_IT_ENABLE 0x0021 +#define BQ27Z746_MAC_CMD_FET_ENABLE 0x0022 +#define BQ27Z746_MAC_CMD_LIFETIMEDATACOLLECTION 0x0023 +#define BQ27Z746_MAC_CMD_LIFETIMEDATARESET 0x0028 +#define BQ27Z746_MAC_CMD_CALIBRATIONMODE 0x002D +#define BQ27Z746_MAC_CMD_LIFETIMEDATAFLUSH 0x002E +#define BQ27Z746_MAC_CMD_LIFETIMEDATASPEEDUPMODE 0x002F +#define BQ27Z746_MAC_CMD_SEALDEVICE 0x0030 +#define BQ27Z746_MAC_CMD_SECURITYKEYS 0x0035 +#define BQ27Z746_MAC_CMD_RESET2 0x0041 +#define BQ27Z746_MAC_CMD_TAMBIENTSYNC 0x0047 +#define BQ27Z746_MAC_CMD_DEVICE_NAME 0x004A +#define BQ27Z746_MAC_CMD_DEVICE_CHEM 0x004B +#define BQ27Z746_MAC_CMD_MANUFACTURER_NAME 0x004C +#define BQ27Z746_MAC_CMD_MANUFACTURE_DATE 0x004D +#define BQ27Z746_MAC_CMD_SERIAL_NUMBER 0x004E +#define BQ27Z746_MAC_CMD_SAFETYALERT 0x0050 +#define BQ27Z746_MAC_CMD_SAFETYSTATUS 0x0051 +#define BQ27Z746_MAC_CMD_OPERATIONSTATUS 0x0054 +#define BQ27Z746_MAC_CMD_CHARGINGSTATUS 0x0055 +#define BQ27Z746_MAC_CMD_GAUGINGSTATUS 0x0056 +#define BQ27Z746_MAC_CMD_MANUFACTURINGSTATUS 0x0057 +#define BQ27Z746_MAC_CMD_LIFETIMEDATABLOCK1 0x0060 +#define BQ27Z746_MAC_CMD_LIFETIMEDATABLOCK2 0x0061 +#define BQ27Z746_MAC_CMD_LIFETIMEDATABLOCK3 0x0062 +#define BQ27Z746_MAC_CMD_LIFETIMEDATABLOCK4 0x0063 +#define BQ27Z746_MAC_CMD_LIFETIMEDATABLOCK6 0x0065 +#define BQ27Z746_MAC_CMD_LIFETIMEDATABLOCK7 0x0065 +#define BQ27Z746_MAC_CMD_LIFETIMEDATABLOCK8 0x0067 +#define BQ27Z746_MAC_CMD_LIFETIMEDATABLOCK9 0x0068 +#define BQ27Z746_MAC_CMD_LIFETIMEDATABLOCK10 0x0069 +#define BQ27Z746_MAC_CMD_LIFETIMEDATABLOCK11 0x006A +#define BQ27Z746_MAC_CMD_LIFETIMEDATABLOCK12 0x006B +#define BQ27Z746_MAC_CMD_MANUFACTURERINFO 0x0070 +#define BQ27Z746_MAC_CMD_DASTATUS1 0x0071 +#define BQ27Z746_MAC_CMD_DASTATUS2 0x0072 +#define BQ27Z746_MAC_CMD_ITSTATUS1 0x0073 +#define BQ27Z746_MAC_CMD_ITSTATUS2 0x0074 +#define BQ27Z746_MAC_CMD_ITSTATUS3 0x0075 +#define BQ27Z746_MAC_CMD_FCC_SOH 0x0077 +#define BQ27Z746_MAC_CMD_FILTERED_CAPACITY 0x0078 +#define BQ27Z746_MAC_CMD_MANUFACTURERINFOB 0x007A +#define BQ27Z746_MAC_CMD_MANUFACTURERINFOC 0x007B +#define BQ27Z746_MAC_CMD_FET_CONTROL_OVERRIDE 0x0097 +#define BQ27Z746_MAC_CMD_SYSTEM_RESET_ENABLE 0x00A3 +#define BQ27Z746_MAC_CMD_SYSTEM_RESET 0x00A4 +#define BQ27Z746_MAC_CMD_BATTSENSEOUTPUT 0x00B1 +#define BQ27Z746_MAC_CMD_RATABLECELL0 0x00E0 +#define BQ27Z746_MAC_CMD_ROMMODE 0x0F00 +#define BQ27Z746_MAC_CMD_DATAFLASHACCESS 0x4000 +#define BQ27Z746_MAC_CMD_SWITCHTOHDQ 0x7C40 +#define BQ27Z746_MAC_CMD_EXITCALIBRATIONOUTPUT 0xF080 +#define BQ27Z746_MAC_CMD_OUTPUTCCANDADCFORCALIBRATIO 0xF081 +#define BQ27Z746_MAC_CMD_OUTPUTTEMPERATURECAL 0xF083 +#define BQ27Z746_MAC_CMD_PROTECTORCALIBRATION 0xF0A0 +#define BQ27Z746_MAC_CMD_PROTECTORIMAGE1 0xF0A1 +#define BQ27Z746_MAC_CMD_PROTECTORIMAGE2 0xF0A2 +#define BQ27Z746_MAC_CMD_PROTECTORIMAGESAVE 0xF0A3 +#define BQ27Z746_MAC_CMD_PROTECTORIMAGELOCK 0xF0A4 +#define BQ27Z746_MAC_CMD_PROTECTORFACTORYCONFIG 0xF0A5 + +struct bq27z746_config { + struct i2c_dt_spec i2c; +}; + +#endif diff --git a/drivers/fuel_gauge/bq27z746/emul_bq27z746.c b/drivers/fuel_gauge/bq27z746/emul_bq27z746.c new file mode 100644 index 00000000000..fe4256cdea5 --- /dev/null +++ b/drivers/fuel_gauge/bq27z746/emul_bq27z746.c @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2023, ithinx GmbH + * Copyright (c) 2023, Tonies GmbH + * + * SPDX-License-Identifier: Apache-2.0 + * + * Emulator for bq27z746 fuel gauge + */ + +#include +#define DT_DRV_COMPAT ti_bq27z746 + +#include +LOG_MODULE_REGISTER(EMUL_BQ27Z746); + +#include +#include +#include +#include +#include + +#include "bq27z746.h" + +#define BQ27Z746_MAC_DATA_LEN 32 +#define BQ27Z746_MAC_OVERHEAD_LEN 4 /* 2 cmd bytes, 1 length byte, 1 checksum byte */ +#define BQ27Z746_MAC_COMPLETE_LEN (BQ27Z746_MAC_DATA_LEN + BQ27Z746_MAC_OVERHEAD_LEN) + +struct bq27z746_emul_data { + uint16_t mac_cmd; +}; + +/** Static configuration for the emulator */ +struct bq27z746_emul_cfg { + /** I2C address of emulator */ + uint16_t addr; +}; + +static int emul_bq27z746_read_altmac(const struct emul *target, uint8_t *buf, size_t len) +{ + const uint8_t manufacturer_name[] = "Texas Instruments"; + const uint8_t device_name[] = "BQ27Z746"; + const uint8_t device_chemistry[] = "LION"; + const struct bq27z746_emul_data *data = target->data; + + if (len < BQ27Z746_MAC_COMPLETE_LEN) { + LOG_ERR("When reading the ALTMAC, one must read the full %u byte", + BQ27Z746_MAC_COMPLETE_LEN); + return -EIO; + } + + memset(buf, 0, len); + + /* + * The data read from BQ27Z746_ALTMANUFACTURERACCESS is: + * 0..1: The command (for verification) + * 2..33: The data + * 34: Checksum calculated as (uint8_t)(0xFF - (sum of all command and data bytes)) + * 35: Length including command, checksum and length (e.g. data length + 4) + */ + + /* Put the command in the first two byte */ + sys_put_le16(data->mac_cmd, buf); + + /* Based on the command, put some data and the length into the buffer. */ + /* In all of the operations, don't consider the zero-terminator. */ + switch (data->mac_cmd) { + case BQ27Z746_MAC_CMD_MANUFACTURER_NAME: + memcpy(&buf[2], manufacturer_name, sizeof(manufacturer_name) - 1); + buf[35] = sizeof(manufacturer_name) - 1 + BQ27Z746_MAC_OVERHEAD_LEN; + break; + case BQ27Z746_MAC_CMD_DEVICE_NAME: + memcpy(&buf[2], device_name, sizeof(device_name) - 1); + buf[35] = sizeof(device_name) - 1 + BQ27Z746_MAC_OVERHEAD_LEN; + break; + case BQ27Z746_MAC_CMD_DEVICE_CHEM: + memcpy(&buf[2], device_chemistry, sizeof(device_chemistry) - 1); + buf[35] = sizeof(device_chemistry) - 1 + BQ27Z746_MAC_OVERHEAD_LEN; + break; + default: + LOG_ERR("ALTMAC command 0x%x is not supported", data->mac_cmd); + return -EIO; + } + + /* Calculate the checksum */ + uint8_t sum = 0; /* Intentionally 8 bit wide and overflowing */ + + for (int i = 0; i < BQ27Z746_MAC_COMPLETE_LEN - 2; i++) { + sum += buf[i]; + } + buf[34] = 0xFF - sum; + + return 0; +} + +static int emul_bq27z746_write(const struct emul *target, uint8_t *buf, size_t len) +{ + struct bq27z746_emul_data *data = target->data; + const uint8_t reg = buf[0]; + + switch (reg) { + case BQ27Z746_ALTMANUFACTURERACCESS: + data->mac_cmd = sys_get_le16(&buf[1]); + return 0; + default: + LOG_ERR("Writing is only supported to ALTMAC currently"); + return -EIO; + } +} + +static int emul_bq27z746_reg_read(const struct emul *target, int reg, int *val) +{ + switch (reg) { + case BQ27Z746_MANUFACTURERACCESS: + *val = 1; + break; + case BQ27Z746_ATRATE: + *val = -2; + break; + case BQ27Z746_ATRATETIMETOEMPTY: + *val = 1; + break; + case BQ27Z746_TEMPERATURE: + *val = 1; + break; + case BQ27Z746_VOLTAGE: + *val = 1; + break; + case BQ27Z746_BATTERYSTATUS: + *val = 1; + break; + case BQ27Z746_CURRENT: + *val = -2; + break; + case BQ27Z746_REMAININGCAPACITY: + *val = 1; + break; + case BQ27Z746_FULLCHARGECAPACITY: + *val = 1; + break; + case BQ27Z746_AVERAGECURRENT: + *val = -2; + break; + case BQ27Z746_AVERAGETIMETOEMPTY: + *val = 1; + break; + case BQ27Z746_AVERAGETIMETOFULL: + *val = 1; + break; + case BQ27Z746_MAXLOADCURRENT: + *val = 1; + break; + case BQ27Z746_MAXLOADTIMETOEMPTY: + *val = 1; + break; + case BQ27Z746_AVERAGEPOWER: + *val = 1; + break; + case BQ27Z746_BTPDISCHARGESET: + *val = 1; + break; + case BQ27Z746_BTPCHARGESET: + *val = 1; + break; + case BQ27Z746_INTERNALTEMPERATURE: + *val = 1; + break; + case BQ27Z746_CYCLECOUNT: + *val = 1; + break; + case BQ27Z746_RELATIVESTATEOFCHARGE: + *val = 1; + break; + case BQ27Z746_STATEOFHEALTH: + *val = 1; + break; + case BQ27Z746_CHARGINGVOLTAGE: + *val = 1; + break; + case BQ27Z746_CHARGINGCURRENT: + *val = 1; + break; + case BQ27Z746_TERMINATEVOLTAGE: + *val = 1; + break; + case BQ27Z746_TIMESTAMPUPPER: + *val = 1; + break; + case BQ27Z746_TIMESTAMPLOWER: + *val = 1; + break; + case BQ27Z746_QMAXCYCLES: + *val = 1; + break; + case BQ27Z746_DESIGNCAPACITY: + *val = 1; + break; + case BQ27Z746_ALTMANUFACTURERACCESS: + *val = 1; + break; + case BQ27Z746_MACDATA: + *val = 1; + break; + case BQ27Z746_MACDATASUM: + *val = 1; + break; + case BQ27Z746_MACDATALEN: + *val = 1; + break; + case BQ27Z746_VOLTHISETTHRESHOLD: + *val = 1; + break; + case BQ27Z746_VOLTHICLEARTHRESHOLD: + *val = 1; + break; + case BQ27Z746_VOLTLOSETTHRESHOLD: + *val = 1; + break; + case BQ27Z746_VOLTLOCLEARTHRESHOLD: + *val = 1; + break; + case BQ27Z746_TEMPHISETTHRESHOLD: + *val = 1; + break; + case BQ27Z746_TEMPHICLEARTHRESHOLD: + *val = 1; + break; + case BQ27Z746_TEMPLOSETTHRESHOLD: + *val = 1; + break; + case BQ27Z746_TEMPLOCLEARTHRESHOLD: + *val = 1; + break; + case BQ27Z746_INTERRUPTSTATUS: + *val = 1; + break; + case BQ27Z746_SOCDELTASETTHRESHOLD: + *val = 1; + break; + default: + LOG_ERR("Unknown register 0x%x read", reg); + return -EIO; + } + LOG_INF("read 0x%x = 0x%x", reg, *val); + + return 0; +} + +static int emul_bq27z746_read(const struct emul *target, int reg, uint8_t *buf, size_t len) +{ + if (len == 2) { + unsigned int val; + int rc = emul_bq27z746_reg_read(target, reg, &val); + + if (rc) { + return rc; + } + + sys_put_le16(val, buf); + } else { + switch (reg) { + case BQ27Z746_ALTMANUFACTURERACCESS: + LOG_DBG("Reading %u byte from ALTMAC", len); + emul_bq27z746_read_altmac(target, buf, len); + break; + default: + LOG_ERR("Reading is only supported from ALTMAC currently"); + return -EIO; + } + } + + return 0; +} + +static int bq27z746_emul_transfer_i2c(const struct emul *target, struct i2c_msg *msgs, int num_msgs, + int addr) +{ + int reg; + int rc; + + __ASSERT_NO_MSG(msgs && num_msgs); + + i2c_dump_msgs_rw("emul", msgs, num_msgs, addr, false); + switch (num_msgs) { + case 1: + if (msgs->flags & I2C_MSG_READ) { + LOG_ERR("Unexpected read"); + return -EIO; + } + + return emul_bq27z746_write(target, msgs->buf, msgs->len); + case 2: + if (msgs->flags & I2C_MSG_READ) { + LOG_ERR("Unexpected read"); + return -EIO; + } + if (msgs->len != 1) { + LOG_ERR("Unexpected msg0 length %d", msgs->len); + return -EIO; + } + reg = msgs->buf[0]; + + /* Now process the 'read' part of the message */ + msgs++; + if (msgs->flags & I2C_MSG_READ) { + rc = emul_bq27z746_read(target, reg, msgs->buf, msgs->len); + if (rc) { + return rc; + } + } else { + LOG_ERR("Second message must be an I2C write"); + return -EIO; + } + return rc; + default: + LOG_ERR("Invalid number of messages: %d", num_msgs); + return -EIO; + } + + return 0; +} + +static const struct i2c_emul_api bq27z746_emul_api_i2c = { + .transfer = bq27z746_emul_transfer_i2c, +}; + +/** + * Set up a new emulator (I2C) + * + * @param emul Emulation information + * @param parent Device to emulate + * @return 0 indicating success (always) + */ +static int emul_bq27z746_init(const struct emul *target, const struct device *parent) +{ + ARG_UNUSED(target); + ARG_UNUSED(parent); + + return 0; +} + +/* + * Main instantiation macro. + */ +#define BQ27Z746_EMUL(n) \ + static struct bq27z746_emul_data bq27z746_emul_data_##n; \ + static const struct bq27z746_emul_cfg bq27z746_emul_cfg_##n = { \ + .addr = DT_INST_REG_ADDR(n), \ + }; \ + EMUL_DT_INST_DEFINE(n, emul_bq27z746_init, &bq27z746_emul_data_##n, \ + &bq27z746_emul_cfg_##n, &bq27z746_emul_api_i2c, NULL) + +DT_INST_FOREACH_STATUS_OKAY(BQ27Z746_EMUL) diff --git a/dts/bindings/fuel-gauge/ti,bq27z746.yaml b/dts/bindings/fuel-gauge/ti,bq27z746.yaml new file mode 100644 index 00000000000..3de382ca7c3 --- /dev/null +++ b/dts/bindings/fuel-gauge/ti,bq27z746.yaml @@ -0,0 +1,13 @@ +# Copyright (c) 2023, ithinx GmbH +# Copyright (c) 2023, Tonies GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +description: | + Texas Instruments BQ27Z746 fuel gauge. For more info visit + https://www.ti.com/product/BQ27Z746 + + +compatible: "ti,bq27z746" + +include: [i2c-device.yaml, fuel-gauge.yaml] diff --git a/tests/drivers/fuel_gauge/bq27z746/CMakeLists.txt b/tests/drivers/fuel_gauge/bq27z746/CMakeLists.txt new file mode 100644 index 00000000000..24070133dbd --- /dev/null +++ b/tests/drivers/fuel_gauge/bq27z746/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_bq27z746.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/tests/drivers/fuel_gauge/bq27z746/boards/native_posix.conf b/tests/drivers/fuel_gauge/bq27z746/boards/native_posix.conf new file mode 100644 index 00000000000..022a71dd0f0 --- /dev/null +++ b/tests/drivers/fuel_gauge/bq27z746/boards/native_posix.conf @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_EMUL=y diff --git a/tests/drivers/fuel_gauge/bq27z746/boards/native_posix.overlay b/tests/drivers/fuel_gauge/bq27z746/boards/native_posix.overlay new file mode 100644 index 00000000000..2dc85db341a --- /dev/null +++ b/tests/drivers/fuel_gauge/bq27z746/boards/native_posix.overlay @@ -0,0 +1,11 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +&i2c0 { + bq27z746: bq27z746@55 { + compatible = "ti,bq27z746"; + reg = <0x55>; + status = "okay"; + }; +}; diff --git a/tests/drivers/fuel_gauge/bq27z746/prj.conf b/tests/drivers/fuel_gauge/bq27z746/prj.conf new file mode 100644 index 00000000000..23ceb0b3cdd --- /dev/null +++ b/tests/drivers/fuel_gauge/bq27z746/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/bq27z746/src/test_bq27z746.c b/tests/drivers/fuel_gauge/bq27z746/src/test_bq27z746.c new file mode 100644 index 00000000000..88d8b59c9f4 --- /dev/null +++ b/tests/drivers/fuel_gauge/bq27z746/src/test_bq27z746.c @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2023, ithinx GmbH + * Copyright (c) 2023, Tonies GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct bq27z746_fixture { + const struct device *dev; + const struct fuel_gauge_driver_api *api; +}; + +static void *bq27z746_setup(void) +{ + static ZTEST_DMEM struct bq27z746_fixture fixture; + + fixture.dev = DEVICE_DT_GET_ANY(ti_bq27z746); + k_object_access_all_grant(fixture.dev); + + zassert_true(device_is_ready(fixture.dev), "Fuel Gauge not found"); + + return &fixture; +} + +ZTEST_USER_F(bq27z746, test_get_all_props_failed_returns_negative) +{ + struct fuel_gauge_get_property props[] = { + { + /* Invalid property */ + .property_type = FUEL_GAUGE_PROP_MAX, + }, + }; + + int ret = fuel_gauge_get_prop(fixture->dev, props, ARRAY_SIZE(props)); + + zassert_equal(props[0].status, -ENOTSUP, "Getting bad property %d has a good status.", + props[0].property_type); + + zassert_true(ret < 0); +} + +ZTEST_USER_F(bq27z746, test_get_some_props_failed_returns_failed_prop_count) +{ + struct fuel_gauge_get_property props[] = { + { + /* First invalid property */ + .property_type = FUEL_GAUGE_PROP_MAX, + }, + { + /* Second invalid property */ + .property_type = FUEL_GAUGE_PROP_MAX, + }, + { + /* Valid property */ + .property_type = FUEL_GAUGE_VOLTAGE, + }, + + }; + + int ret = fuel_gauge_get_prop(fixture->dev, props, ARRAY_SIZE(props)); + + zassert_equal(props[0].status, -ENOTSUP, "Getting bad property %d has a good status.", + props[0].property_type); + + zassert_equal(props[1].status, -ENOTSUP, "Getting bad property %d has a good status.", + props[1].property_type); + + zassert_ok(props[2].status, "Property %d getting %d has a bad status.", 2, + props[2].property_type); + + zassert_equal(ret, 2); +} + +ZTEST_USER_F(bq27z746, test_get_buffer_prop) +{ + struct fuel_gauge_get_buffer_property prop; + int ret; + + { + struct sbs_gauge_manufacturer_name mfg_name; + + prop.property_type = FUEL_GAUGE_MANUFACTURER_NAME; + ret = fuel_gauge_get_buffer_prop(fixture->dev, &prop, &mfg_name, sizeof(mfg_name)); + zassert_ok(ret); + zassert_ok(prop.status, "Property %d has a bad status.", prop.property_type); +#if CONFIG_EMUL + /* Only test for fixed values in emulation since the real device might be */ + /* reprogrammed and respond with different values */ + zassert_equal(sizeof("Texas Instruments") - 1, mfg_name.manufacturer_name_length); + zassert_mem_equal(mfg_name.manufacturer_name, "Texas Instruments", + mfg_name.manufacturer_name_length, + "mfg_name.manufacturer_name='%s'", mfg_name.manufacturer_name); +#endif + } + { + struct sbs_gauge_device_name dev_name; + + prop.property_type = FUEL_GAUGE_DEVICE_NAME; + ret = fuel_gauge_get_buffer_prop(fixture->dev, &prop, &dev_name, sizeof(dev_name)); + zassert_ok(ret); + zassert_ok(prop.status, "Property %d has a bad status.", prop.property_type); +#if CONFIG_EMUL + /* Only test for fixed values in emulation since the real device might be */ + /* reprogrammed and respond with different values */ + zassert_equal(sizeof("BQ27Z746") - 1, dev_name.device_name_length); + zassert_mem_equal(dev_name.device_name, "BQ27Z746", dev_name.device_name_length); +#endif + } + { + struct sbs_gauge_device_chemistry device_chemistry; + + prop.property_type = FUEL_GAUGE_DEVICE_CHEMISTRY; + ret = fuel_gauge_get_buffer_prop(fixture->dev, &prop, &device_chemistry, + sizeof(device_chemistry)); + zassert_ok(ret); + zassert_ok(prop.status, "Property %d has a bad status.", prop.property_type); +#if CONFIG_EMUL + /* Only test for fixed values in emulation since the real device might be */ + /* reprogrammed and respond with different values */ + zassert_equal(sizeof("LION") - 1, device_chemistry.device_chemistry_length); + zassert_mem_equal(device_chemistry.device_chemistry, "LION", + device_chemistry.device_chemistry_length); +#endif + } +} + +ZTEST_USER_F(bq27z746, test_get_props__returns_ok) +{ + /* Validate what props are supported by the driver */ + + struct fuel_gauge_get_property props[] = { + { + .property_type = FUEL_GAUGE_AVG_CURRENT, + }, + { + .property_type = FUEL_GAUGE_CYCLE_COUNT, + }, + { + .property_type = FUEL_GAUGE_CURRENT, + }, + { + .property_type = FUEL_GAUGE_FULL_CHARGE_CAPACITY, + }, + { + .property_type = FUEL_GAUGE_REMAINING_CAPACITY, + }, + { + .property_type = FUEL_GAUGE_RUNTIME_TO_EMPTY, + }, + { + .property_type = FUEL_GAUGE_RUNTIME_TO_FULL, + }, + { + .property_type = FUEL_GAUGE_SBS_MFR_ACCESS, + }, + { + .property_type = FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE, + }, + { + .property_type = FUEL_GAUGE_TEMPERATURE, + }, + { + .property_type = FUEL_GAUGE_VOLTAGE, + }, + { + .property_type = FUEL_GAUGE_SBS_ATRATE, + }, + { + .property_type = FUEL_GAUGE_SBS_ATRATE_TIME_TO_EMPTY, + }, + { + .property_type = FUEL_GAUGE_CHARGE_VOLTAGE, + }, + { + .property_type = FUEL_GAUGE_CHARGE_CURRENT, + }, + { + .property_type = FUEL_GAUGE_STATUS, + }, + { + .property_type = FUEL_GAUGE_DESIGN_CAPACITY, + }, + }; + + int ret = fuel_gauge_get_prop(fixture->dev, props, ARRAY_SIZE(props)); + + /* All props shall have a good status */ + for (int i = 0; i < ARRAY_SIZE(props); i++) { + zassert_ok(props[i].status, "Property %d getting %d has a bad status.", i, + props[i].property_type); + } + + /* Check properties for valid ranges */ +#if CONFIG_EMUL + /* When emulating, check for the fixed values coming from the emulator */ + zassert_equal(props[0].value.avg_current, -2000); + zassert_equal(props[1].value.cycle_count, 100); + zassert_equal(props[2].value.current, -2000); + zassert_equal(props[3].value.full_charge_capacity, 1000); + zassert_equal(props[4].value.remaining_capacity, 1000); + zassert_equal(props[5].value.runtime_to_empty, 1); + zassert_equal(props[6].value.runtime_to_full, 1); + zassert_equal(props[7].value.sbs_mfr_access_word, 1); + zassert_equal(props[8].value.relative_state_of_charge, 1); + zassert_equal(props[9].value.temperature, 1); + zassert_equal(props[10].value.voltage, 1000); + zassert_equal(props[11].value.sbs_at_rate, -2); + zassert_equal(props[12].value.sbs_at_rate_time_to_empty, 1); + zassert_equal(props[13].value.chg_voltage, 1); + zassert_equal(props[14].value.chg_current, 1); + zassert_equal(props[15].value.fg_status, 1); + zassert_equal(props[16].value.design_cap, 1); +#else + /* When having a real device, check for the valid ranges */ + zassert_between_inclusive(props[0].value.avg_current, -32768 * 1000, 32767 * 1000); + zassert_between_inclusive(props[1].value.cycle_count, 0, 6553500); + zassert_between_inclusive(props[2].value.current, -32768 * 1000, 32767 * 1000); + zassert_between_inclusive(props[3].value.full_charge_capacity, 0, 32767 * 1000); + zassert_between_inclusive(props[4].value.remaining_capacity, 0, 32767 * 1000); + zassert_between_inclusive(props[5].value.runtime_to_empty, 0, 65535); + zassert_between_inclusive(props[6].value.runtime_to_full, 0, 65535); + /* Not testing props[7]. This is the manufacturer access and has only status bits */ + zassert_between_inclusive(props[8].value.relative_state_of_charge, 0, 100); + zassert_between_inclusive(props[9].value.temperature, 0, 32767); + zassert_between_inclusive(props[10].value.voltage, 0, 32767 * 1000); + zassert_between_inclusive(props[11].value.sbs_at_rate, -32768, 32767); + zassert_between_inclusive(props[12].value.sbs_at_rate_time_to_empty, 0, 65535); + zassert_between_inclusive(props[13].value.chg_voltage, 0, 32767); + zassert_between_inclusive(props[14].value.chg_current, 0, 32767); + /* Not testing props[15]. This property is the status and only has only status bits */ + zassert_between_inclusive(props[16].value.design_cap, 0, 32767); +#endif + + zassert_ok(ret); +} + +ZTEST_SUITE(bq27z746, NULL, bq27z746_setup, NULL, NULL, NULL); diff --git a/tests/drivers/fuel_gauge/bq27z746/testcase.yaml b/tests/drivers/fuel_gauge/bq27z746/testcase.yaml new file mode 100644 index 00000000000..6d9096b3a98 --- /dev/null +++ b/tests/drivers/fuel_gauge/bq27z746/testcase.yaml @@ -0,0 +1,5 @@ +tests: + # section.subsection + drivers.bq27z746: + filter: dt_compat_enabled("ti,bq27z746") + platform_allow: native_posix