summaryrefslogtreecommitdiff
path: root/zephyr/test/drivers/sm5803/src/sm5803.c
diff options
context:
space:
mode:
Diffstat (limited to 'zephyr/test/drivers/sm5803/src/sm5803.c')
-rw-r--r--zephyr/test/drivers/sm5803/src/sm5803.c709
1 files changed, 709 insertions, 0 deletions
diff --git a/zephyr/test/drivers/sm5803/src/sm5803.c b/zephyr/test/drivers/sm5803/src/sm5803.c
new file mode 100644
index 0000000000..b9d4e17505
--- /dev/null
+++ b/zephyr/test/drivers/sm5803/src/sm5803.c
@@ -0,0 +1,709 @@
+/* Copyright 2023 The ChromiumOS Authors
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "battery_smart.h"
+#include "charger.h"
+#include "driver/charger/sm5803.h"
+#include "emul/emul_common_i2c.h"
+#include "emul/emul_sm5803.h"
+#include "emul/tcpc/emul_tcpci_partner_src.h"
+#include "test/drivers/charger_utils.h"
+#include "test/drivers/test_state.h"
+#include "test/drivers/utils.h"
+
+#include <zephyr/drivers/emul.h>
+#include <zephyr/ztest.h>
+
+#define CHARGER_NUM get_charger_num(&sm5803_drv)
+#define SM5803_EMUL EMUL_DT_GET(DT_NODELABEL(sm5803_emul))
+
+ZTEST_SUITE(sm5803, drivers_predicate_post_main, NULL, NULL, NULL, NULL);
+
+ZTEST(sm5803, test_chip_id)
+{
+ int id;
+
+ /* Emulator only implements chip revision 3. */
+ zassert_ok(sm5803_drv.device_id(CHARGER_NUM, &id));
+ zassert_equal(id, 3);
+
+ /* After a successful read, the value is cached. */
+ i2c_common_emul_set_read_fail_reg(sm5803_emul_get_i2c_main(SM5803_EMUL),
+ SM5803_REG_CHIP_ID);
+ zassert_ok(sm5803_drv.device_id(CHARGER_NUM, &id));
+}
+
+struct i2c_log_entry {
+ bool write;
+ uint8_t i2c_addr;
+ uint8_t reg_addr;
+ uint8_t value;
+};
+
+struct i2c_log {
+ struct i2c_log_entry entries[128];
+ size_t entries_used;
+ size_t entries_asserted;
+};
+
+#define LOG_ASSERT(_write, _i2c_addr, _reg_addr, _value) \
+ do { \
+ zassert_true(log_ptr->entries_asserted < \
+ log_ptr->entries_used, \
+ "No more I2C transactions to verify " \
+ "(logged %d)", \
+ log_ptr->entries_used); \
+ size_t i = log_ptr->entries_asserted++; \
+ struct i2c_log_entry *entry = &log_ptr->entries[i]; \
+ \
+ zassert(entry->write == _write && \
+ entry->i2c_addr == _i2c_addr && \
+ entry->reg_addr == _reg_addr && \
+ (!_write || entry->value == _value), \
+ "I2C log mismatch", \
+ "Transaction %d did not match expectations:\n" \
+ "expected %5s of address %#04x register" \
+ " %#04x with value %#04x\n" \
+ " found %s of address %#04x register" \
+ " %#04x with value %#04x", \
+ i, _write ? "write" : "read", _i2c_addr, _reg_addr, \
+ _value, entry->write ? "write" : "read", \
+ entry->i2c_addr, entry->reg_addr, entry->value); \
+ } while (0)
+
+#define LOG_ASSERT_R(_i2c_addr, _reg_addr) \
+ LOG_ASSERT(false, _i2c_addr, _reg_addr, 0)
+#define LOG_ASSERT_W(_i2c_addr, _reg_addr, _value) \
+ LOG_ASSERT(true, _i2c_addr, _reg_addr, _value)
+#define LOG_ASSERT_RW(_i2c_addr, _reg_addr, _value) \
+ do { \
+ LOG_ASSERT_R(_i2c_addr, _reg_addr); \
+ LOG_ASSERT_W(_i2c_addr, _reg_addr, _value); \
+ } while (0)
+
+/*
+ * Generate a function for each I2C address to log the correct address, because
+ * the target pointer is the same for each I2C address and there's no way to
+ * determine from parameters what address we're meant to be.
+ */
+#define DEFINE_LOG_FNS(name, addr) \
+ static int i2c_log_write_##name(const struct emul *target, int reg, \
+ uint8_t val, int bytes, void *ctx) \
+ { \
+ struct i2c_log *log = ctx; \
+ \
+ if (log->entries_used >= ARRAY_SIZE(log->entries)) { \
+ return -ENOSPC; \
+ } \
+ \
+ zassert_equal(target->bus_type, EMUL_BUS_TYPE_I2C); \
+ log->entries[log->entries_used++] = (struct i2c_log_entry){ \
+ .write = true, \
+ .i2c_addr = addr, \
+ .reg_addr = reg, \
+ .value = val, \
+ }; \
+ \
+ /* Discard write. */ \
+ return 0; \
+ } \
+ \
+ static int i2c_log_read_##name(const struct emul *target, int reg, \
+ uint8_t *val, int bytes, void *ctx) \
+ { \
+ struct i2c_log *log = ctx; \
+ \
+ if (log->entries_used >= ARRAY_SIZE(log->entries)) { \
+ return -ENOSPC; \
+ } \
+ \
+ zassert_equal(target->bus_type, EMUL_BUS_TYPE_I2C); \
+ log->entries[log->entries_used++] = (struct i2c_log_entry){ \
+ .write = false, \
+ .i2c_addr = addr, \
+ .reg_addr = reg, \
+ }; \
+ \
+ /* Fall through to emulator read. */ \
+ return 1; \
+ }
+
+DEFINE_LOG_FNS(main, SM5803_ADDR_MAIN_FLAGS);
+DEFINE_LOG_FNS(meas, SM5803_ADDR_MEAS_FLAGS);
+DEFINE_LOG_FNS(chg, SM5803_ADDR_CHARGER_FLAGS);
+DEFINE_LOG_FNS(test, SM5803_ADDR_TEST_FLAGS);
+
+static void configure_i2c_log(const struct emul *emul, struct i2c_log *log)
+{
+ i2c_common_emul_set_read_func(sm5803_emul_get_i2c_main(emul),
+ i2c_log_read_main, log);
+ i2c_common_emul_set_write_func(sm5803_emul_get_i2c_main(emul),
+ i2c_log_write_main, log);
+ i2c_common_emul_set_read_func(sm5803_emul_get_i2c_meas(emul),
+ i2c_log_read_meas, log);
+ i2c_common_emul_set_write_func(sm5803_emul_get_i2c_meas(emul),
+ i2c_log_write_meas, log);
+ i2c_common_emul_set_read_func(sm5803_emul_get_i2c_chg(emul),
+ i2c_log_read_chg, log);
+ i2c_common_emul_set_write_func(sm5803_emul_get_i2c_chg(emul),
+ i2c_log_write_chg, log);
+ i2c_common_emul_set_read_func(sm5803_emul_get_i2c_test(emul),
+ i2c_log_read_test, log);
+ i2c_common_emul_set_write_func(sm5803_emul_get_i2c_test(emul),
+ i2c_log_write_test, log);
+}
+
+static void verify_init_common(struct i2c_log *const log_ptr)
+{
+ /* Enable LDOs */
+ LOG_ASSERT_RW(SM5803_ADDR_MAIN_FLAGS, SM5803_REG_REFERENCE, 0);
+ /* Psys DAC */
+ LOG_ASSERT_RW(SM5803_ADDR_MEAS_FLAGS, SM5803_REG_PSYS1, 0x05);
+ /* ADC sigma delta */
+ LOG_ASSERT_RW(SM5803_ADDR_CHARGER_FLAGS, SM5803_REG_CC_CONFIG1, 0x09);
+ /* PROCHOT comparators */
+ LOG_ASSERT_RW(SM5803_ADDR_CHARGER_FLAGS, SM5803_REG_PHOT1, 0x2d);
+ /* DPM voltage */
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, SM5803_REG_DPM_VL_SET_MSB,
+ 0x12);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, SM5803_REG_DPM_VL_SET_LSB,
+ 0x04);
+ /* Default input current limit */
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, SM5803_REG_CHG_ILIM, 0x05);
+ /* Interrupts */
+ LOG_ASSERT_W(SM5803_ADDR_MAIN_FLAGS, SM5803_REG_INT1_EN, 0x04);
+ LOG_ASSERT_W(SM5803_ADDR_MAIN_FLAGS, SM5803_REG_INT4_EN, 0x13);
+ LOG_ASSERT_W(SM5803_ADDR_MEAS_FLAGS, SM5803_REG_TINT_HIGH_TH, 0xd1);
+ LOG_ASSERT_W(SM5803_ADDR_MEAS_FLAGS, SM5803_REG_TINT_LOW_TH, 0);
+ LOG_ASSERT_RW(SM5803_ADDR_MAIN_FLAGS, SM5803_REG_INT2_EN, 0x81);
+ /* Charging is exclusively EC-controlled */
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, SM5803_REG_FLOW2, 0x40);
+ /* Battery parameters */
+ LOG_ASSERT_RW(SM5803_ADDR_CHARGER_FLAGS, SM5803_REG_FAST_CONF5, 0x02);
+ LOG_ASSERT_RW(SM5803_ADDR_CHARGER_FLAGS, SM5803_REG_PRE_FAST_CONF_REG1,
+ 0);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, SM5803_REG_PRECHG, 0x02);
+ /* BFET limits */
+ LOG_ASSERT_W(SM5803_ADDR_MEAS_FLAGS, SM5803_REG_BFET_PWR_MAX_TH, 0x33);
+ LOG_ASSERT_W(SM5803_ADDR_MEAS_FLAGS, SM5803_REG_BFET_PWR_HWSAFE_MAX_TH,
+ 0xcd);
+ LOG_ASSERT_RW(SM5803_ADDR_MAIN_FLAGS, SM5803_REG_INT3_EN, 0x06);
+ LOG_ASSERT_RW(SM5803_ADDR_CHARGER_FLAGS, SM5803_REG_FLOW3, 0);
+ LOG_ASSERT_RW(SM5803_ADDR_CHARGER_FLAGS, SM5803_REG_SWITCHER_CONF,
+ 0x01);
+}
+
+/**
+ * Driver internal "init completed" flag needs to be cleared to actually run
+ * init.
+ */
+extern bool chip_inited[1];
+/** Driver internal cached value of chip device ID. */
+extern int dev_id;
+
+ZTEST(sm5803, test_init_2s)
+{
+ struct i2c_log log = {};
+ struct i2c_log *const log_ptr = &log;
+
+ /* Hook up logging functions for each I2C address. */
+ configure_i2c_log(SM5803_EMUL, &log);
+
+ /* Emulator defaults to 2S PMODE so we don't need to set it. */
+ chip_inited[0] = false;
+ sm5803_drv.init(CHARGER_NUM);
+
+ /* Ensures we're in a safe state for operation. */
+ LOG_ASSERT_R(SM5803_ADDR_MAIN_FLAGS, SM5803_REG_CLOCK_SEL);
+ LOG_ASSERT_W(SM5803_ADDR_MEAS_FLAGS, SM5803_REG_GPADC_CONFIG1, 0xf7);
+ /* Checks VBUS presence and disables charger. */
+ LOG_ASSERT_R(SM5803_ADDR_MEAS_FLAGS, SM5803_REG_GPADC_CONFIG1);
+ LOG_ASSERT_R(SM5803_ADDR_MEAS_FLAGS, SM5803_REG_VBUS_MEAS_MSB);
+ LOG_ASSERT_R(SM5803_ADDR_MEAS_FLAGS, SM5803_REG_VBUS_MEAS_LSB);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, SM5803_REG_FLOW1, 0);
+ /* Gets chip ID (already cached) and PMODE. */
+ LOG_ASSERT_R(SM5803_ADDR_MAIN_FLAGS, SM5803_REG_PLATFORM);
+ /* Writes a lot of registers for presumably important reasons. */
+ LOG_ASSERT_W(SM5803_ADDR_MEAS_FLAGS, 0x26, 0xdc);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x21, 0x9b);
+ LOG_ASSERT_W(SM5803_ADDR_MAIN_FLAGS, 0x30, 0xc0);
+ LOG_ASSERT_W(SM5803_ADDR_MAIN_FLAGS, 0x80, 0x01);
+ LOG_ASSERT_W(SM5803_ADDR_MAIN_FLAGS, 0x1a, 0x08);
+ LOG_ASSERT_W(SM5803_ADDR_MEAS_FLAGS, 0x08, 0xc2);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x1d, 0x40);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x22, 0xb3);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x3e, 0x3c);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x4f, 0xbf);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x52, 0x77);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x53, 0xD2);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x54, 0x02);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x55, 0xD1);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x56, 0x7F);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x57, 0x01);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x58, 0x50);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x59, 0x7F);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x5A, 0x13);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x5B, 0x52);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x5D, 0xD0);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x60, 0x44);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x65, 0x35);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x66, 0x29);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x7D, 0x97);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x7E, 0x07);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x33, 0x3C);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x5C, 0x7A);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x73, 0x22);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x50, 0x88);
+ LOG_ASSERT_RW(SM5803_ADDR_CHARGER_FLAGS, 0x34, 0x80);
+ LOG_ASSERT_W(SM5803_ADDR_MAIN_FLAGS, 0x1f, 0x01);
+ LOG_ASSERT_W(SM5803_ADDR_TEST_FLAGS, 0x43, 0x10);
+ LOG_ASSERT_W(SM5803_ADDR_TEST_FLAGS, 0x47, 0x10);
+ LOG_ASSERT_W(SM5803_ADDR_TEST_FLAGS, 0x48, 0x04);
+ LOG_ASSERT_W(SM5803_ADDR_MAIN_FLAGS, 0x1f, 0);
+ verify_init_common(log_ptr);
+
+ zassert_equal(log.entries_asserted, log.entries_used,
+ "recorded %d transactions but only verified %d",
+ log.entries_used, log.entries_asserted);
+
+ /*
+ * Running init again should check and update VBUS presence but not
+ * re-run complete initialization. Doing more than that probably means
+ * the first init failed.
+ */
+ log.entries_used = 0;
+ sm5803_drv.init(CHARGER_NUM);
+ zassert_equal(log.entries_used, 6);
+}
+
+ZTEST(sm5803, test_init_3s)
+{
+ struct i2c_log log = {};
+ struct i2c_log *const log_ptr = &log;
+
+ /* Hook up logging functions for each I2C address. */
+ configure_i2c_log(SM5803_EMUL, &log);
+
+ /* Set 3S PMODE and run init */
+ chip_inited[0] = false;
+ sm5803_emul_set_pmode(SM5803_EMUL, 0x14);
+ sm5803_drv.init(CHARGER_NUM);
+
+ /* Ensures we're in a safe state for operation. */
+ LOG_ASSERT_R(SM5803_ADDR_MAIN_FLAGS, SM5803_REG_CLOCK_SEL);
+ LOG_ASSERT_W(SM5803_ADDR_MEAS_FLAGS, SM5803_REG_GPADC_CONFIG1, 0xf7);
+ /* Checks VBUS presence and disables charger. */
+ LOG_ASSERT_R(SM5803_ADDR_MEAS_FLAGS, SM5803_REG_GPADC_CONFIG1);
+ LOG_ASSERT_R(SM5803_ADDR_MEAS_FLAGS, SM5803_REG_VBUS_MEAS_MSB);
+ LOG_ASSERT_R(SM5803_ADDR_MEAS_FLAGS, SM5803_REG_VBUS_MEAS_LSB);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, SM5803_REG_FLOW1, 0);
+ /* Gets chip ID (already cached) and PMODE. */
+ LOG_ASSERT_R(SM5803_ADDR_MAIN_FLAGS, SM5803_REG_PLATFORM);
+ /* Writes a lot of registers for presumably important reasons. */
+ LOG_ASSERT_W(SM5803_ADDR_MEAS_FLAGS, 0x26, 0xd8);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x21, 0x9b);
+ LOG_ASSERT_W(SM5803_ADDR_MAIN_FLAGS, 0x30, 0xc0);
+ LOG_ASSERT_W(SM5803_ADDR_MAIN_FLAGS, 0x80, 0x01);
+ LOG_ASSERT_W(SM5803_ADDR_MAIN_FLAGS, 0x1a, 0x08);
+ LOG_ASSERT_W(SM5803_ADDR_MEAS_FLAGS, 0x08, 0xc2);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x1d, 0x40);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x22, 0xb3);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x3e, 0x3c);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x4b, 0xa6);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x4f, 0xbf);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x52, 0x77);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x53, 0xD2);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x54, 0x02);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x55, 0xD1);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x56, 0x7F);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x57, 0x01);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x58, 0x50);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x59, 0x7F);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x5A, 0x13);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x5B, 0x50);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x5D, 0xB0);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x60, 0x44);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x65, 0x35);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x66, 0x29);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x7D, 0x67);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x7E, 0x04);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x33, 0x3C);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x5C, 0x7A);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x73, 0x22);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x50, 0x88);
+ LOG_ASSERT_RW(SM5803_ADDR_CHARGER_FLAGS, 0x34, 0x80);
+ LOG_ASSERT_W(SM5803_ADDR_MAIN_FLAGS, 0x1f, 0x01);
+ LOG_ASSERT_W(SM5803_ADDR_TEST_FLAGS, 0x43, 0x10);
+ LOG_ASSERT_W(SM5803_ADDR_TEST_FLAGS, 0x47, 0x10);
+ LOG_ASSERT_W(SM5803_ADDR_TEST_FLAGS, 0x48, 0x04);
+ LOG_ASSERT_W(SM5803_ADDR_MAIN_FLAGS, 0x1f, 0);
+ verify_init_common(log_ptr);
+
+ zassert_equal(log.entries_asserted, log.entries_used,
+ "recorded %d transactions but only verified %d",
+ log.entries_used, log.entries_asserted);
+}
+
+ZTEST(sm5803, test_init_rev2)
+{
+ struct i2c_log log = {};
+ struct i2c_log *const log_ptr = &log;
+
+ /* Hook up logging functions for each I2C address. */
+ configure_i2c_log(SM5803_EMUL, &log);
+
+ chip_inited[0] = false;
+ dev_id = -1;
+ sm5803_emul_set_device_id(SM5803_EMUL, 2);
+ sm5803_drv.init(CHARGER_NUM);
+
+ /* Ensures we're in a safe state for operation. */
+ LOG_ASSERT_R(SM5803_ADDR_MAIN_FLAGS, SM5803_REG_CLOCK_SEL);
+ LOG_ASSERT_W(SM5803_ADDR_MEAS_FLAGS, SM5803_REG_GPADC_CONFIG1, 0xf7);
+ /* Checks VBUS presence and disables charger. */
+ LOG_ASSERT_R(SM5803_ADDR_MEAS_FLAGS, SM5803_REG_GPADC_CONFIG1);
+ LOG_ASSERT_R(SM5803_ADDR_MEAS_FLAGS, SM5803_REG_VBUS_MEAS_MSB);
+ LOG_ASSERT_R(SM5803_ADDR_MEAS_FLAGS, SM5803_REG_VBUS_MEAS_LSB);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, SM5803_REG_FLOW1, 0);
+ /* Gets chip ID */
+ LOG_ASSERT_R(SM5803_ADDR_MAIN_FLAGS, SM5803_REG_CHIP_ID);
+ /* Writes a lot of registers for presumably important reasons. */
+ LOG_ASSERT_W(SM5803_ADDR_MAIN_FLAGS, 0x20, 0x08);
+ LOG_ASSERT_W(SM5803_ADDR_MAIN_FLAGS, 0x30, 0xc0);
+ LOG_ASSERT_W(SM5803_ADDR_MAIN_FLAGS, 0x80, 0x01);
+ LOG_ASSERT_W(SM5803_ADDR_MEAS_FLAGS, 0x08, 0xc2);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x1d, 0x40);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x1f, 0x09);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x22, 0xb3);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x23, 0x81);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x28, 0xb7);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x4a, 0x82);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x4b, 0xa3);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x4c, 0xa8);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x4d, 0xca);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x4e, 0x07);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x4f, 0xff);
+
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x50, 0x98);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x51, 0);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x52, 0x77);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x53, 0xd2);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x54, 0x02);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x55, 0xd1);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x56, 0x7f);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x57, 0x02);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x58, 0xd1);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x59, 0x7f);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x5a, 0x13);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x5b, 0x50);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x5c, 0x5b);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x5d, 0xb0);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x5e, 0x3c);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x5f, 0x3c);
+
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x60, 0x44);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x61, 0x20);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x65, 0x35);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x66, 0x29);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x67, 0x64);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x68, 0x88);
+ LOG_ASSERT_W(SM5803_ADDR_CHARGER_FLAGS, 0x69, 0xc7);
+
+ LOG_ASSERT_W(SM5803_ADDR_MAIN_FLAGS, 0x1f, 1);
+ LOG_ASSERT_RW(SM5803_ADDR_TEST_FLAGS, 0x8e, 0x20);
+ LOG_ASSERT_W(SM5803_ADDR_MAIN_FLAGS, 0x1f, 0);
+
+ verify_init_common(log_ptr);
+
+ zassert_equal(log.entries_asserted, log.entries_used,
+ "recorded %d transactions but only verified %d",
+ log.entries_used, log.entries_asserted);
+}
+
+ZTEST(sm5803, test_fast_charge_current)
+{
+ int ma;
+
+ /*
+ * Can set and read back charge current limit,
+ * which is adjusted when 0.
+ */
+ zassert_ok(charger_set_current(CHARGER_NUM, 0));
+ zassert_equal(1, sm5803_emul_get_fast_charge_current_limit(SM5803_EMUL),
+ "Zero current limit should be converted to nonzero");
+ zassert_ok(charger_get_current(CHARGER_NUM, &ma));
+ zassert_equal(ma, 100,
+ "Actual current should be 100 mA times register value");
+
+ /* Errors are propagated. */
+ i2c_common_emul_set_read_fail_reg(sm5803_emul_get_i2c_chg(SM5803_EMUL),
+ SM5803_REG_FAST_CONF4);
+ zassert_not_equal(
+ 0, charger_set_current(CHARGER_NUM, 1000),
+ "set_current should fail if FAST_CONF4 is unreadable");
+ zassert_not_equal(
+ 0, charger_get_current(CHARGER_NUM, &ma),
+ "get_current should fail if FAST_CONF4 is unreadable");
+}
+
+ZTEST(sm5803, test_measure_input_current)
+{
+ int ma;
+
+ sm5803_emul_set_input_current(SM5803_EMUL, 852);
+ zassert_ok(charger_get_input_current(CHARGER_NUM, &ma));
+ zassert_equal(ma, 849, "actual returned input current was %d", ma);
+
+ /* Communication errors bubble up */
+ i2c_common_emul_set_read_fail_reg(sm5803_emul_get_i2c_meas(SM5803_EMUL),
+ SM5803_REG_IBUS_CHG_MEAS_LSB);
+ zassert_not_equal(0, charger_get_input_current(CHARGER_NUM, &ma));
+ i2c_common_emul_set_read_fail_reg(sm5803_emul_get_i2c_meas(SM5803_EMUL),
+ SM5803_REG_IBUS_CHG_MEAS_MSB);
+ zassert_not_equal(0, charger_get_input_current(CHARGER_NUM, &ma));
+}
+
+ZTEST(sm5803, test_input_current_limit)
+{
+ int icl;
+ bool reached;
+
+ /* Can set and read back the input current limit. */
+ zassert_ok(charger_set_input_current_limit(CHARGER_NUM, 2150));
+ zassert_equal(21, sm5803_emul_read_chg_reg(SM5803_EMUL,
+ SM5803_REG_CHG_ILIM));
+ zassert_ok(charger_get_input_current_limit(CHARGER_NUM, &icl));
+ zassert_equal(2100, icl,
+ "expected 2100 mA input current limit, but was %d", icl);
+
+ /* Can also check whether input current is limited. */
+ zassert_ok(charger_is_icl_reached(CHARGER_NUM, &reached));
+ zassert_false(reached);
+ sm5803_emul_set_input_current(SM5803_EMUL, 2400);
+ zassert_ok(charger_is_icl_reached(CHARGER_NUM, &reached));
+ zassert_true(reached);
+
+ /* Communication errors bubble up. */
+ i2c_common_emul_set_read_fail_reg(sm5803_emul_get_i2c_chg(SM5803_EMUL),
+ SM5803_REG_CHG_ILIM);
+ zassert_not_equal(0,
+ charger_get_input_current_limit(CHARGER_NUM, &icl));
+ i2c_common_emul_set_write_fail_reg(sm5803_emul_get_i2c_chg(SM5803_EMUL),
+ SM5803_REG_CHG_ILIM);
+ zassert_not_equal(0,
+ charger_set_input_current_limit(CHARGER_NUM, 1400));
+}
+
+/* Analog measurement of VBUS. */
+ZTEST(sm5803, test_get_vbus_voltage)
+{
+ int mv;
+
+ /* Regular measurement with VBUS ADC enabled works. */
+ sm5803_emul_set_vbus_voltage(SM5803_EMUL, 5032);
+ zassert_ok(charger_get_vbus_voltage(CHARGER_NUM, &mv));
+ /* 5.031 is the nearest value representable by the VBUS ADC. */
+ zassert_equal(mv, 5031, "driver reported %d mV VBUS", mv);
+
+ /* Communication errors for ADC value bubble up. */
+ i2c_common_emul_set_read_fail_reg(sm5803_emul_get_i2c_meas(SM5803_EMUL),
+ SM5803_REG_VBUS_MEAS_LSB);
+ zassert_not_equal(0, charger_get_vbus_voltage(CHARGER_NUM, &mv));
+ i2c_common_emul_set_read_fail_reg(sm5803_emul_get_i2c_meas(SM5803_EMUL),
+ SM5803_REG_VBUS_MEAS_MSB);
+ zassert_not_equal(0, charger_get_vbus_voltage(CHARGER_NUM, &mv));
+
+ /* Returns a NOT_POWERED error if the VBUS ADC is disabled. */
+ sm5803_emul_set_gpadc_conf(SM5803_EMUL,
+ (uint8_t)~SM5803_GPADCC1_VBUS_EN, 0);
+ zassert_equal(EC_ERROR_NOT_POWERED,
+ charger_get_vbus_voltage(CHARGER_NUM, &mv));
+}
+
+ZTEST(sm5803, test_get_battery_current)
+{
+ int ma;
+
+ sm5803_emul_set_battery_current(SM5803_EMUL, 1234);
+ zassert_ok(charger_get_actual_current(CHARGER_NUM, &ma));
+ /* 1229 mA is nearest value representable at ADC resolution */
+ zassert_equal(ma, 1229, "read value was %d", ma);
+
+ /* Communication errors bubble up. */
+ i2c_common_emul_set_read_fail_reg(sm5803_emul_get_i2c_meas(SM5803_EMUL),
+ SM5803_REG_IBAT_CHG_AVG_MEAS_LSB);
+ zassert_not_equal(0, charger_get_actual_current(CHARGER_NUM, &ma));
+ i2c_common_emul_set_read_fail_reg(sm5803_emul_get_i2c_meas(SM5803_EMUL),
+ SM5803_REG_IBAT_CHG_AVG_MEAS_MSB);
+ zassert_not_equal(0, charger_get_actual_current(CHARGER_NUM, &ma));
+}
+
+/* Digital VBUS presence detection derived from DHG_DET. */
+ZTEST(sm5803, test_digital_vbus_presence_detect)
+{
+ /*
+ * CHG_DET going high (from VBUS presence) triggers an interrupt and
+ * presence update.
+ */
+ sm5803_emul_set_vbus_voltage(SM5803_EMUL, 5000);
+ k_sleep(K_SECONDS(1)); /* Allow interrupt to be serviced. */
+ zassert_true(sm5803_is_vbus_present(CHARGER_NUM));
+
+ /* VBUS going away triggers another interrupt and update. */
+ sm5803_emul_set_vbus_voltage(SM5803_EMUL, 0);
+ k_sleep(K_SECONDS(1)); /* Allow interrupt to be serviced. */
+ zassert_false(sm5803_is_vbus_present(CHARGER_NUM));
+}
+
+/* VBUS detection for PD, analog or digital depending on chip state. */
+ZTEST(sm5803, test_check_vbus_level)
+{
+ /* Default state with VBUS ADC enabled: uses analog value */
+ zassert_true(sm5803_check_vbus_level(CHARGER_NUM, VBUS_REMOVED));
+ sm5803_emul_set_vbus_voltage(SM5803_EMUL, 5000);
+ zassert_true(sm5803_check_vbus_level(CHARGER_NUM, VBUS_PRESENT));
+
+ /* 4.6V is less than vSafe5V */
+ sm5803_emul_set_vbus_voltage(SM5803_EMUL, 4600);
+ k_sleep(K_SECONDS(1));
+ zassert_false(sm5803_check_vbus_level(CHARGER_NUM, VBUS_PRESENT));
+
+ /*
+ * With ADC disabled, uses digital presence only. 4.6V is high enough
+ * to trip CHG_DET but wasn't enough to count as present with the analog
+ * reading.
+ */
+ sm5803_emul_set_gpadc_conf(SM5803_EMUL, 0, 0);
+ zassert_true(sm5803_check_vbus_level(CHARGER_NUM, VBUS_PRESENT));
+
+ /* 0.4V is !CHG_DET */
+ sm5803_emul_set_vbus_voltage(SM5803_EMUL, 400);
+ k_sleep(K_SECONDS(1));
+ zassert_true(sm5803_check_vbus_level(CHARGER_NUM, VBUS_REMOVED));
+}
+
+ZTEST(sm5803, test_lpm)
+{
+ const struct emul *tcpci_emul = EMUL_GET_USBC_BINDING(0, tcpc);
+ struct tcpci_partner_data partner;
+ struct tcpci_src_emul_data partner_src;
+ uint8_t gpadc1, gpadc2;
+ uint8_t cc_conf1;
+ uint8_t flow1, flow2, flow3;
+
+ tcpci_partner_init(&partner, PD_REV30);
+ partner.extensions = tcpci_src_emul_init(&partner_src, &partner, NULL);
+
+ /* Connect 5V source. */
+ zassert_ok(tcpci_partner_connect_to_tcpci(&partner, tcpci_emul));
+ sm5803_emul_set_vbus_voltage(SM5803_EMUL, 5000);
+ k_sleep(K_SECONDS(4));
+
+ /* Charger should now have exited runtime LPM. */
+ zassert_false(sm5803_emul_is_clock_slowed(SM5803_EMUL));
+ sm5803_emul_get_gpadc_conf(SM5803_EMUL, &gpadc1, &gpadc2);
+ /* All except IBAT_DISCHG enabled. */
+ zassert_equal(gpadc1, 0xf7, "actual value was %#x", gpadc1);
+ /* Default value. */
+ zassert_equal(gpadc2, 1, "actual value was %#x", gpadc2);
+ /* Sigma-delta for Coulomb Counter is enabled. */
+ cc_conf1 = sm5803_emul_get_cc_config(SM5803_EMUL);
+ zassert_equal(cc_conf1, 0x09, "actual value was %#x", cc_conf1);
+ /* Charger is sinking. */
+ sm5803_emul_get_flow_regs(SM5803_EMUL, &flow1, &flow2, &flow3);
+ zassert_equal(flow1, 0x01, "FLOW1 should be set for sinking, was %#x",
+ flow1);
+
+ /* Disconnect source, causing charger to go to runtime LPM. */
+ zassert_ok(tcpci_emul_disconnect_partner(tcpci_emul));
+ sm5803_emul_set_vbus_voltage(SM5803_EMUL, 24);
+ k_sleep(K_SECONDS(4));
+
+ zassert_true(sm5803_emul_is_clock_slowed(SM5803_EMUL));
+ /* Sigma delta was disabled. */
+ cc_conf1 = sm5803_emul_get_cc_config(SM5803_EMUL);
+ zassert_equal(sm5803_emul_get_cc_config(SM5803_EMUL), 0x01,
+ "actual value was %#x", cc_conf1);
+ /*
+ * Runtime LPM hook runs before the charge manager updates, so we expect
+ * the GPADCs to be left on because the charger is still set for sinking
+ * when it goes to runtime LPM.
+ */
+ sm5803_emul_get_gpadc_conf(SM5803_EMUL, &gpadc1, &gpadc2);
+ zassert_equal(gpadc1, 0xf7, "actual value was %#x", gpadc1);
+ zassert_equal(gpadc2, 1, "actual value was %#x", gpadc2);
+
+ /*
+ * Reconnect the source and inhibit charging, so GPADCs can be disabled
+ * when we disconnect it.
+ */
+ zassert_ok(tcpci_partner_connect_to_tcpci(&partner, tcpci_emul));
+ sm5803_emul_set_vbus_voltage(SM5803_EMUL, 5010);
+ k_sleep(K_SECONDS(4));
+ zassert_ok(charger_set_mode(CHARGE_FLAG_INHIBIT_CHARGE));
+ zassert_ok(tcpci_emul_disconnect_partner(tcpci_emul));
+ sm5803_emul_set_vbus_voltage(SM5803_EMUL, 0);
+ k_sleep(K_SECONDS(4));
+
+ /* This time LPM actually did disable the GPADCs. */
+ sm5803_emul_get_gpadc_conf(SM5803_EMUL, &gpadc1, &gpadc2);
+ zassert_equal(gpadc1, 0, "actual value was %#x", gpadc1);
+ zassert_equal(gpadc2, 0, "actual value was %#x", gpadc2);
+}
+
+ZTEST(sm5803, test_get_battery_cells)
+{
+ int cells;
+
+ /* Default PMODE reports 2s */
+ zassert_ok(sm5803_drv.get_battery_cells(CHARGER_NUM, &cells));
+ zassert_equal(cells, 2);
+
+ /* 3s PMODE is 3s */
+ sm5803_emul_set_pmode(SM5803_EMUL, 0x14);
+ zassert_ok(sm5803_drv.get_battery_cells(CHARGER_NUM, &cells));
+ zassert_equal(cells, 3);
+
+ /* Unrecognized PMODE is an error */
+ sm5803_emul_set_pmode(SM5803_EMUL, 0x1f);
+ zassert_not_equal(sm5803_drv.get_battery_cells(CHARGER_NUM, &cells), 0);
+ zassert_equal(cells, -1);
+
+ /* Communication error bubbles up */
+ i2c_common_emul_set_read_fail_reg(sm5803_emul_get_i2c_main(SM5803_EMUL),
+ SM5803_REG_PLATFORM);
+ zassert_not_equal(sm5803_drv.get_battery_cells(CHARGER_NUM, &cells), 0);
+}
+
+ZTEST(sm5803, test_gpio)
+{
+ /* Open drain output */
+ zassert_ok(sm5803_configure_gpio0(CHARGER_NUM, GPIO0_MODE_OUTPUT, 1));
+ zassert_equal(sm5803_emul_get_gpio_ctrl(SM5803_EMUL), 0x42);
+ /* Set output high, from default of low. */
+ zassert_ok(sm5803_set_gpio0_level(CHARGER_NUM, 1));
+ zassert_equal(sm5803_emul_get_gpio_ctrl(SM5803_EMUL), 0x43);
+ /* Set it low again. */
+ zassert_ok(sm5803_set_gpio0_level(CHARGER_NUM, 0));
+ zassert_equal(sm5803_emul_get_gpio_ctrl(SM5803_EMUL), 0x42);
+
+ /* Push-pull prochot. */
+ zassert_ok(sm5803_configure_gpio0(CHARGER_NUM, GPIO0_MODE_PROCHOT, 0));
+ zassert_equal(sm5803_emul_get_gpio_ctrl(SM5803_EMUL), 0x00);
+
+ /* CHG_DET output enable lives in this register too */
+ zassert_ok(sm5803_configure_chg_det_od(CHARGER_NUM, 1));
+ zassert_equal(sm5803_emul_get_gpio_ctrl(SM5803_EMUL), 0x80);
+ zassert_ok(sm5803_configure_chg_det_od(CHARGER_NUM, 0));
+ zassert_equal(sm5803_emul_get_gpio_ctrl(SM5803_EMUL), 0x00);
+
+ /* Communication errors bubble up */
+ i2c_common_emul_set_read_fail_reg(sm5803_emul_get_i2c_main(SM5803_EMUL),
+ SM5803_REG_GPIO0_CTRL);
+ zassert_not_equal(
+ sm5803_configure_gpio0(CHARGER_NUM, GPIO0_MODE_INPUT, 0), 0);
+ zassert_not_equal(sm5803_set_gpio0_level(CHARGER_NUM, 0), 0);
+ zassert_not_equal(sm5803_configure_chg_det_od(CHARGER_NUM, 1), 0);
+}