157 lines
3.5 KiB
C
157 lines
3.5 KiB
C
/*
|
|
* Copyright 2023, Alvaro Garcia <maxpowel@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Emulator for max17048 fuel gauge
|
|
*/
|
|
|
|
|
|
#define DT_DRV_COMPAT maxim_max17048
|
|
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(maxim_max17048);
|
|
|
|
#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 "max17048.h"
|
|
|
|
static int crate_value = 0x4000;
|
|
|
|
void emul_max17048_set_crate_status(int value)
|
|
{
|
|
crate_value = value;
|
|
}
|
|
|
|
/** Static configuration for the emulator */
|
|
struct max17048_emul_cfg {
|
|
/** I2C address of emulator */
|
|
uint16_t addr;
|
|
};
|
|
|
|
static int emul_max17048_reg_write(const struct emul *target, int reg, int val)
|
|
{
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
static int emul_max17048_reg_read(const struct emul *target, int reg, int *val)
|
|
{
|
|
|
|
switch (reg) {
|
|
case REGISTER_VERSION:
|
|
*val = 0x1000;
|
|
break;
|
|
case REGISTER_CRATE:
|
|
*val = crate_value;
|
|
break;
|
|
case REGISTER_SOC:
|
|
*val = 0x3525;
|
|
break;
|
|
case REGISTER_VCELL:
|
|
*val = 0x4387;
|
|
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 max17048_emul_transfer_i2c(const struct emul *target, struct i2c_msg *msgs,
|
|
int num_msgs, int addr)
|
|
{
|
|
/* Largely copied from emul_bmi160.c */
|
|
unsigned int val;
|
|
int reg;
|
|
int rc;
|
|
|
|
__ASSERT_NO_MSG(msgs && num_msgs);
|
|
|
|
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_max17048_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_max17048_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 max17048_emul_api_i2c = {
|
|
.transfer = max17048_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_max17048_init(const struct emul *target, const struct device *parent)
|
|
{
|
|
ARG_UNUSED(target);
|
|
ARG_UNUSED(parent);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Main instantiation macro.
|
|
*/
|
|
#define MAX17048_EMUL(n) \
|
|
static const struct max17048_emul_cfg max17048_emul_cfg_##n = { \
|
|
.addr = DT_INST_REG_ADDR(n), \
|
|
}; \
|
|
EMUL_DT_INST_DEFINE(n, emul_max17048_init, NULL, \
|
|
&max17048_emul_cfg_##n, &max17048_emul_api_i2c, NULL)
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(MAX17048_EMUL)
|