1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2023 Carl Zeiss Meditec AG
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #define DT_DRV_COMPAT adi_adltc2990
7 
8 #include <zephyr/device.h>
9 #include <zephyr/drivers/emul.h>
10 #include <zephyr/drivers/emul_sensor.h>
11 #include <zephyr/drivers/i2c.h>
12 #include <zephyr/drivers/i2c_emul.h>
13 #include <zephyr/logging/log.h>
14 #include <zephyr/sys/util.h>
15 
16 #include "adltc2990.h"
17 #include "adltc2990_reg.h"
18 #include "adltc2990_emul.h"
19 
20 LOG_MODULE_DECLARE(adltc2990, CONFIG_SENSOR_LOG_LEVEL);
21 
22 #define ADLTC2990_NUM_REGS ADLTC2990_REG_VCC_LSB
23 
24 struct adltc2990_emul_data {
25 	uint8_t reg[ADLTC2990_NUM_REGS];
26 };
27 
28 struct adltc2990_emul_cfg {
29 };
30 
adltc2990_emul_set_reg(const struct emul * target,uint8_t reg_addr,const uint8_t * val)31 void adltc2990_emul_set_reg(const struct emul *target, uint8_t reg_addr, const uint8_t *val)
32 {
33 	struct adltc2990_emul_data *data = target->data;
34 
35 	__ASSERT_NO_MSG(reg_addr <= ADLTC2990_NUM_REGS);
36 	memcpy(data->reg + reg_addr, val, 1);
37 }
38 
adltc2990_emul_get_reg(const struct emul * target,uint8_t reg_addr,uint8_t * val)39 void adltc2990_emul_get_reg(const struct emul *target, uint8_t reg_addr, uint8_t *val)
40 {
41 	struct adltc2990_emul_data *data = target->data;
42 
43 	__ASSERT_NO_MSG(reg_addr <= ADLTC2990_NUM_REGS);
44 	memcpy(val, data->reg + reg_addr, 1);
45 }
46 
adltc2990_emul_reset(const struct emul * target)47 void adltc2990_emul_reset(const struct emul *target)
48 {
49 	struct adltc2990_emul_data *data = target->data;
50 
51 	memset(data->reg, 0, ADLTC2990_NUM_REGS);
52 }
53 
adltc2990_emul_handle_write(const struct emul * target,uint8_t regn,uint8_t value)54 static int adltc2990_emul_handle_write(const struct emul *target, uint8_t regn, uint8_t value)
55 {
56 	struct adltc2990_emul_data *data = target->data;
57 
58 	switch (regn) {
59 	case ADLTC2990_REG_CONTROL:
60 		data->reg[ADLTC2990_REG_CONTROL] = value;
61 		break;
62 
63 	case ADLTC2990_REG_TRIGGER:
64 		data->reg[ADLTC2990_REG_TRIGGER] = value;
65 		break;
66 
67 	default:
68 		break;
69 	}
70 	return 0;
71 }
72 
adltc2990_emul_transfer_i2c(const struct emul * target,struct i2c_msg * msgs,int num_msgs,int addr)73 static int adltc2990_emul_transfer_i2c(const struct emul *target, struct i2c_msg *msgs,
74 				       int num_msgs, int addr)
75 {
76 	struct adltc2990_emul_data *data = target->data;
77 
78 	i2c_dump_msgs_rw(target->dev, msgs, num_msgs, addr, false);
79 
80 	if (num_msgs < 1) {
81 		LOG_ERR("Invalid number of messages: %d", num_msgs);
82 		return -EIO;
83 	}
84 	if (FIELD_GET(I2C_MSG_READ, msgs->flags)) {
85 		LOG_ERR("Unexpected read");
86 		return -EIO;
87 	}
88 	if (msgs->len < 1) {
89 		LOG_ERR("Unexpected msg0 length %d", msgs->len);
90 		return -EIO;
91 	}
92 
93 	uint8_t regn = msgs->buf[0];
94 	bool is_read = FIELD_GET(I2C_MSG_READ, msgs->flags) == 1;
95 	bool is_stop = FIELD_GET(I2C_MSG_STOP, msgs->flags) == 1;
96 
97 	if (!is_stop && !is_read) {
98 		/* First message was a write with the register number, check next message */
99 		msgs++;
100 		is_read = FIELD_GET(I2C_MSG_READ, msgs->flags) == 1;
101 		is_stop = FIELD_GET(I2C_MSG_STOP, msgs->flags) == 1;
102 	}
103 
104 	if (is_read) {
105 		for (int i = 0; i < msgs->len; ++i) {
106 			msgs->buf[i] = data->reg[regn + i];
107 		}
108 	} else {
109 		int rc = adltc2990_emul_handle_write(target, regn, msgs->buf[1]);
110 
111 		if (rc != 0) {
112 			return rc;
113 		}
114 	}
115 	return 0;
116 };
117 
adltc2990_emul_init(const struct emul * target,const struct device * parent)118 static int adltc2990_emul_init(const struct emul *target, const struct device *parent)
119 {
120 	ARG_UNUSED(parent);
121 	adltc2990_emul_reset(target);
122 
123 	return 0;
124 }
125 
126 static const struct i2c_emul_api adltc2990_emul_api_i2c = {
127 	.transfer = adltc2990_emul_transfer_i2c,
128 };
129 
130 #define ADLTC2990_EMUL(n)                                                                          \
131 	const struct adltc2990_emul_cfg adltc2990_emul_cfg_##n;                                    \
132 	struct adltc2990_emul_data adltc2990_emul_data_##n;                                        \
133 	EMUL_DT_INST_DEFINE(n, adltc2990_emul_init, &adltc2990_emul_data_##n,                      \
134 			    &adltc2990_emul_cfg_##n, &adltc2990_emul_api_i2c, NULL)
135 
136 DT_INST_FOREACH_STATUS_OKAY(ADLTC2990_EMUL)
137