/* * Copyright 2023 Cirrus Logic, Inc. * * SPDX-License-Identifier: Apache-2.0 * * Emulator for SBS 1.1 compliant smart battery charger. */ #define DT_DRV_COMPAT sbs_sbs_charger #include #include #include #include #include #include #include "sbs_charger.h" LOG_MODULE_REGISTER(sbs_sbs_charger); /** Static configuration for the emulator */ struct sbs_charger_emul_cfg { /** I2C address of emulator */ uint16_t addr; }; /** Run-time data used by the emulator */ struct sbs_charger_emul_data { uint16_t reg_charger_mode; }; static int emul_sbs_charger_reg_write(const struct emul *target, int reg, int val) { struct sbs_charger_emul_data *data = target->data; LOG_INF("write %x = %x", reg, val); switch (reg) { case SBS_CHARGER_REG_CHARGER_MODE: data->reg_charger_mode = val; break; default: LOG_ERR("Unknown write %x", reg); return -EIO; } return 0; } static int emul_sbs_charger_reg_read(const struct emul *target, int reg, int *val) { switch (reg) { case SBS_CHARGER_REG_SPEC_INFO: case SBS_CHARGER_REG_CHARGER_MODE: case SBS_CHARGER_REG_STATUS: case SBS_CHARGER_REG_ALARM_WARNING: /* Arbitrary stub value. */ *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 sbs_charger_emul_transfer_i2c(const struct emul *target, struct i2c_msg *msgs, int num_msgs, int addr) { /* Largely copied from emul_sbs_gauge.c */ struct sbs_charger_emul_data *data; unsigned int val; int reg; int rc; data = target->data; i2c_dump_msgs_rw(target->dev, msgs, num_msgs, addr, false); switch (num_msgs) { 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) { switch (msgs->len - 1) { case 1: rc = emul_sbs_charger_reg_read(target, reg, &val); if (rc) { /* Return before writing bad value to message buffer */ return rc; } /* SBS uses SMBus, which sends data in little-endian format. */ sys_put_le16(val, msgs->buf); break; default: LOG_ERR("Unexpected msg1 length %d", msgs->len); return -EIO; } } else { /* We write a word (2 bytes by the SBS spec) */ if (msgs->len != 2) { LOG_ERR("Unexpected msg1 length %d", msgs->len); } uint16_t value = sys_get_le16(msgs->buf); rc = emul_sbs_charger_reg_write(target, reg, value); } break; default: LOG_ERR("Invalid number of messages: %d", num_msgs); return -EIO; } return rc; } static const struct i2c_emul_api sbs_charger_emul_api_i2c = { .transfer = sbs_charger_emul_transfer_i2c, }; static int emul_sbs_sbs_charger_init(const struct emul *target, const struct device *parent) { ARG_UNUSED(target); ARG_UNUSED(parent); return 0; } /* * Main instantiation macro. SBS Charger Emulator only implemented for I2C */ #define SBS_CHARGER_EMUL(n) \ static struct sbs_charger_emul_data sbs_charger_emul_data_##n; \ \ static const struct sbs_charger_emul_cfg sbs_charger_emul_cfg_##n = { \ .addr = DT_INST_REG_ADDR(n), \ }; \ EMUL_DT_INST_DEFINE(n, emul_sbs_sbs_charger_init, &sbs_charger_emul_data_##n, \ &sbs_charger_emul_cfg_##n, &sbs_charger_emul_api_i2c, NULL) DT_INST_FOREACH_STATUS_OKAY(SBS_CHARGER_EMUL)