blob: 3f16a2fe67cfc970c19f7d73d260c3608fdd89d5 [file] [log] [blame]
/*
* Copyright (c) 2023 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT asahi_kasei_akm09918c
#include <zephyr/device.h>
#include <zephyr/drivers/emul.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/i2c_emul.h>
#include <zephyr/logging/log.h>
#include "akm09918c_emul.h"
#include "akm09918c_reg.h"
LOG_MODULE_DECLARE(AKM09918C, CONFIG_SENSOR_LOG_LEVEL);
#define NUM_REGS AKM09918C_REG_TS2
struct akm09918c_emul_data {
uint8_t reg[NUM_REGS];
};
struct akm09918c_emul_cfg {
};
void akm09918c_emul_set_reg(const struct emul *target, uint8_t reg_addr, const uint8_t *val,
size_t count)
{
struct akm09918c_emul_data *data = target->data;
__ASSERT_NO_MSG(reg_addr + count < NUM_REGS);
memcpy(data->reg + reg_addr, val, count);
}
void akm09918c_emul_get_reg(const struct emul *target, uint8_t reg_addr, uint8_t *val, size_t count)
{
struct akm09918c_emul_data *data = target->data;
__ASSERT_NO_MSG(reg_addr + count < NUM_REGS);
memcpy(val, data->reg + reg_addr, count);
}
void akm09918c_emul_reset(const struct emul *target)
{
struct akm09918c_emul_data *data = target->data;
memset(data->reg, 0, NUM_REGS);
data->reg[AKM09918C_REG_WIA1] = AKM09918C_WIA1;
data->reg[AKM09918C_REG_WIA2] = AKM09918C_WIA2;
}
static int akm09918c_emul_handle_write(const struct emul *target, uint8_t regn, uint8_t value)
{
struct akm09918c_emul_data *data = target->data;
switch (regn) {
case AKM09918C_REG_CNTL2:
data->reg[AKM09918C_REG_CNTL2] = value;
break;
case AKM09918C_REG_CNTL3:
if (FIELD_GET(AKM09918C_CNTL3_SRST, value) == 1) {
akm09918c_emul_reset(target);
}
break;
}
return 0;
}
static int akm09918c_emul_transfer_i2c(const struct emul *target, struct i2c_msg *msgs,
int num_msgs, int addr)
{
struct akm09918c_emul_data *data = target->data;
i2c_dump_msgs_rw("emul", msgs, num_msgs, addr, false);
if (num_msgs < 1) {
LOG_ERR("Invalid number of messages: %d", num_msgs);
return -EIO;
}
if (FIELD_GET(I2C_MSG_READ, msgs->flags)) {
LOG_ERR("Unexpected read");
return -EIO;
}
if (msgs->len < 1) {
LOG_ERR("Unexpected msg0 length %s", msgs->len);
return -EIO;
}
uint8_t regn = msgs->buf[0];
bool is_read = FIELD_GET(I2C_MSG_READ, msgs->flags) == 1;
bool is_stop = FIELD_GET(I2C_MSG_STOP, msgs->flags) == 1;
if (!is_stop && !is_read) {
/* First message was a write with the register number, check next message */
msgs++;
is_read = FIELD_GET(I2C_MSG_READ, msgs->flags) == 1;
is_stop = FIELD_GET(I2C_MSG_STOP, msgs->flags) == 1;
}
if (is_read) {
/* Read data */
uint8_t mode = data->reg[AKM09918C_REG_CNTL2];
for (int i = 0; i < msgs->len; ++i) {
msgs->buf[i] = data->reg[regn + i];
if (regn + i == AKM09918C_REG_TMPS &&
mode == AKM09918C_CNTL2_SINGLE_MEASURE) {
/* Reading the TMPS register clears the DRDY bit */
data->reg[AKM09918C_REG_ST1] = 0;
}
}
} else {
/* Write data */
int rc = akm09918c_emul_handle_write(target, regn, msgs->buf[1]);
if (rc != 0) {
return rc;
}
}
return 0;
}
static int akm09918c_emul_init(const struct emul *target, const struct device *parent)
{
ARG_UNUSED(parent);
akm09918c_emul_reset(target);
return 0;
}
static const struct i2c_emul_api akm09918c_emul_api_i2c = {
.transfer = akm09918c_emul_transfer_i2c,
};
#define AKM09918C_EMUL(n) \
const struct akm09918c_emul_cfg akm09918c_emul_cfg_##n; \
struct akm09918c_emul_data akm09918c_emul_data_##n; \
EMUL_DT_INST_DEFINE(n, akm09918c_emul_init, &akm09918c_emul_data_##n, \
&akm09918c_emul_cfg_##n, &akm09918c_emul_api_i2c, NULL)
DT_INST_FOREACH_STATUS_OKAY(AKM09918C_EMUL)