drivers: fuelgauge: Add TI BQ27z746 driver
Add driver for the Texas Instruments BQ27z746 fuel gauge Signed-off-by: Marcel Krüger <marcel.krueger@ithinx.io>
This commit is contained in:
parent
d40dbdf96f
commit
387f3c2092
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
6
drivers/fuel_gauge/bq27z746/CMakeLists.txt
Normal file
6
drivers/fuel_gauge/bq27z746/CMakeLists.txt
Normal file
|
@ -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)
|
21
drivers/fuel_gauge/bq27z746/Kconfig
Normal file
21
drivers/fuel_gauge/bq27z746/Kconfig
Normal file
|
@ -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.
|
324
drivers/fuel_gauge/bq27z746/bq27z746.c
Normal file
324
drivers/fuel_gauge/bq27z746/bq27z746.c
Normal file
|
@ -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 <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/fuel_gauge.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <string.h>
|
||||
|
||||
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)
|
142
drivers/fuel_gauge/bq27z746/bq27z746.h
Normal file
142
drivers/fuel_gauge/bq27z746/bq27z746.h
Normal file
|
@ -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 <zephyr/drivers/i2c.h>
|
||||
|
||||
/* 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
|
352
drivers/fuel_gauge/bq27z746/emul_bq27z746.c
Normal file
352
drivers/fuel_gauge/bq27z746/emul_bq27z746.c
Normal file
|
@ -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 <string.h>
|
||||
#define DT_DRV_COMPAT ti_bq27z746
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(EMUL_BQ27Z746);
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/emul.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/drivers/i2c_emul.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
|
||||
#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)
|
13
dts/bindings/fuel-gauge/ti,bq27z746.yaml
Normal file
13
dts/bindings/fuel-gauge/ti,bq27z746.yaml
Normal file
|
@ -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]
|
8
tests/drivers/fuel_gauge/bq27z746/CMakeLists.txt
Normal file
8
tests/drivers/fuel_gauge/bq27z746/CMakeLists.txt
Normal file
|
@ -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})
|
|
@ -0,0 +1,3 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
CONFIG_EMUL=y
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
&i2c0 {
|
||||
bq27z746: bq27z746@55 {
|
||||
compatible = "ti,bq27z746";
|
||||
reg = <0x55>;
|
||||
status = "okay";
|
||||
};
|
||||
};
|
7
tests/drivers/fuel_gauge/bq27z746/prj.conf
Normal file
7
tests/drivers/fuel_gauge/bq27z746/prj.conf
Normal file
|
@ -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
|
246
tests/drivers/fuel_gauge/bq27z746/src/test_bq27z746.c
Normal file
246
tests/drivers/fuel_gauge/bq27z746/src/test_bq27z746.c
Normal file
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
* Copyright (c) 2023, ithinx GmbH
|
||||
* Copyright (c) 2023, Tonies GmbH
|
||||
*
|
||||
* 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/sys/util.h>
|
||||
#include <zephyr/ztest.h>
|
||||
#include <zephyr/ztest_assert.h>
|
||||
|
||||
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);
|
5
tests/drivers/fuel_gauge/bq27z746/testcase.yaml
Normal file
5
tests/drivers/fuel_gauge/bq27z746/testcase.yaml
Normal file
|
@ -0,0 +1,5 @@
|
|||
tests:
|
||||
# section.subsection
|
||||
drivers.bq27z746:
|
||||
filter: dt_compat_enabled("ti,bq27z746")
|
||||
platform_allow: native_posix
|
Loading…
Reference in a new issue