1 /*
2 * Copyright (c) 2023 Google LLC
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 #define DT_DRV_COMPAT asahi_kasei_akm09918c
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 "akm09918c.h"
17 #include "akm09918c_emul.h"
18 #include "akm09918c_reg.h"
19
20 LOG_MODULE_DECLARE(AKM09918C, CONFIG_SENSOR_LOG_LEVEL);
21
22 #define NUM_REGS AKM09918C_REG_TS2
23
24 struct akm09918c_emul_data {
25 uint8_t reg[NUM_REGS];
26 };
27
28 struct akm09918c_emul_cfg {
29 };
30
akm09918c_emul_set_reg(const struct emul * target,uint8_t reg_addr,const uint8_t * val,size_t count)31 void akm09918c_emul_set_reg(const struct emul *target, uint8_t reg_addr, const uint8_t *val,
32 size_t count)
33 {
34 struct akm09918c_emul_data *data = target->data;
35
36 __ASSERT_NO_MSG(reg_addr + count < NUM_REGS);
37 memcpy(data->reg + reg_addr, val, count);
38 }
39
akm09918c_emul_get_reg(const struct emul * target,uint8_t reg_addr,uint8_t * val,size_t count)40 void akm09918c_emul_get_reg(const struct emul *target, uint8_t reg_addr, uint8_t *val, size_t count)
41 {
42 struct akm09918c_emul_data *data = target->data;
43
44 __ASSERT_NO_MSG(reg_addr + count < NUM_REGS);
45 memcpy(val, data->reg + reg_addr, count);
46 }
47
akm09918c_emul_reset(const struct emul * target)48 void akm09918c_emul_reset(const struct emul *target)
49 {
50 struct akm09918c_emul_data *data = target->data;
51
52 memset(data->reg, 0, NUM_REGS);
53 data->reg[AKM09918C_REG_WIA1] = AKM09918C_WIA1;
54 data->reg[AKM09918C_REG_WIA2] = AKM09918C_WIA2;
55 }
56
akm09918c_emul_handle_write(const struct emul * target,uint8_t regn,uint8_t value)57 static int akm09918c_emul_handle_write(const struct emul *target, uint8_t regn, uint8_t value)
58 {
59 struct akm09918c_emul_data *data = target->data;
60
61 switch (regn) {
62 case AKM09918C_REG_CNTL2:
63 data->reg[AKM09918C_REG_CNTL2] = value;
64 break;
65 case AKM09918C_REG_CNTL3:
66 if (FIELD_GET(AKM09918C_CNTL3_SRST, value) == 1) {
67 akm09918c_emul_reset(target);
68 }
69 break;
70 }
71 return 0;
72 }
73
akm09918c_emul_transfer_i2c(const struct emul * target,struct i2c_msg * msgs,int num_msgs,int addr)74 static int akm09918c_emul_transfer_i2c(const struct emul *target, struct i2c_msg *msgs,
75 int num_msgs, int addr)
76 {
77 struct akm09918c_emul_data *data = target->data;
78
79 i2c_dump_msgs_rw(target->dev, msgs, num_msgs, addr, false);
80
81 if (num_msgs < 1) {
82 LOG_ERR("Invalid number of messages: %d", num_msgs);
83 return -EIO;
84 }
85 if (FIELD_GET(I2C_MSG_READ, msgs->flags)) {
86 LOG_ERR("Unexpected read");
87 return -EIO;
88 }
89 if (msgs->len < 1) {
90 LOG_ERR("Unexpected msg0 length %d", msgs->len);
91 return -EIO;
92 }
93
94 uint8_t regn = msgs->buf[0];
95 bool is_read = FIELD_GET(I2C_MSG_READ, msgs->flags) == 1;
96 bool is_stop = FIELD_GET(I2C_MSG_STOP, msgs->flags) == 1;
97
98 if (!is_stop && !is_read) {
99 /* First message was a write with the register number, check next message */
100 msgs++;
101 is_read = FIELD_GET(I2C_MSG_READ, msgs->flags) == 1;
102 is_stop = FIELD_GET(I2C_MSG_STOP, msgs->flags) == 1;
103 }
104 if (is_read) {
105 /* Read data */
106 uint8_t mode = data->reg[AKM09918C_REG_CNTL2];
107
108 for (int i = 0; i < msgs->len; ++i) {
109 msgs->buf[i] = data->reg[regn + i];
110 if (regn + i == AKM09918C_REG_TMPS &&
111 mode == AKM09918C_CNTL2_SINGLE_MEASURE) {
112 /* Reading the TMPS register clears the DRDY bit */
113 data->reg[AKM09918C_REG_ST1] = 0;
114 }
115 }
116 } else {
117 /* Write data */
118 int rc = akm09918c_emul_handle_write(target, regn, msgs->buf[1]);
119
120 if (rc != 0) {
121 return rc;
122 }
123 }
124
125 return 0;
126 }
127
akm09918c_emul_init(const struct emul * target,const struct device * parent)128 static int akm09918c_emul_init(const struct emul *target, const struct device *parent)
129 {
130 ARG_UNUSED(parent);
131 akm09918c_emul_reset(target);
132
133 return 0;
134 }
135
akm09918c_emul_backend_set_channel(const struct emul * target,struct sensor_chan_spec ch,const q31_t * value,int8_t shift)136 static int akm09918c_emul_backend_set_channel(const struct emul *target, struct sensor_chan_spec ch,
137 const q31_t *value, int8_t shift)
138 {
139 if (!target || !target->data) {
140 return -EINVAL;
141 }
142
143 struct akm09918c_emul_data *data = target->data;
144 uint8_t reg;
145
146 switch (ch.chan_type) {
147 case SENSOR_CHAN_MAGN_X:
148 reg = AKM09918C_REG_HXL;
149 break;
150 case SENSOR_CHAN_MAGN_Y:
151 reg = AKM09918C_REG_HYL;
152 break;
153 case SENSOR_CHAN_MAGN_Z:
154 reg = AKM09918C_REG_HZL;
155 break;
156 /* This function only supports setting single channels, so skip MAGN_XYZ */
157 default:
158 return -ENOTSUP;
159 }
160
161 /* Set the ST1 register to show we have data */
162 data->reg[AKM09918C_REG_ST1] |= AKM09918C_ST1_DRDY;
163
164 /* Convert fixed-point Gauss values into microgauss and then into its bit representation */
165 int32_t microgauss =
166 (shift < 0 ? ((int64_t)*value >> -shift) : ((int64_t)*value << shift)) * 1000000 /
167 ((int64_t)INT32_MAX + 1);
168
169 int16_t reg_val =
170 CLAMP(microgauss, AKM09918C_MAGN_MIN_MICRO_GAUSS, AKM09918C_MAGN_MAX_MICRO_GAUSS) /
171 AKM09918C_MICRO_GAUSS_PER_BIT;
172
173 /* Insert reading into registers */
174 data->reg[reg] = reg_val & 0xFF;
175 data->reg[reg + 1] = (reg_val >> 8) & 0xFF;
176
177 return 0;
178 }
179
akm09918c_emul_backend_get_sample_range(const struct emul * target,struct sensor_chan_spec ch,q31_t * lower,q31_t * upper,q31_t * epsilon,int8_t * shift)180 static int akm09918c_emul_backend_get_sample_range(const struct emul *target,
181 struct sensor_chan_spec ch, q31_t *lower,
182 q31_t *upper, q31_t *epsilon, int8_t *shift)
183 {
184 ARG_UNUSED(target);
185
186 if (!lower || !upper || !epsilon || !shift) {
187 return -EINVAL;
188 }
189
190 switch (ch.chan_type) {
191 case SENSOR_CHAN_MAGN_X:
192 case SENSOR_CHAN_MAGN_Y:
193 case SENSOR_CHAN_MAGN_Z:
194 /* +/- 49.12 Gs is the measurement range. 0.0015 Gs is the granularity */
195 *shift = 6;
196 *upper = (int64_t)(49.12 * ((int64_t)INT32_MAX + 1)) >> *shift;
197 *lower = -*upper;
198 *epsilon = (int64_t)(0.0015 * ((int64_t)INT32_MAX + 1)) >> *shift;
199 break;
200 default:
201 return -ENOTSUP;
202 }
203
204 return 0;
205 }
206
207 static const struct i2c_emul_api akm09918c_emul_api_i2c = {
208 .transfer = akm09918c_emul_transfer_i2c,
209 };
210
211 static const struct emul_sensor_driver_api akm09918c_emul_sensor_driver_api = {
212 .set_channel = akm09918c_emul_backend_set_channel,
213 .get_sample_range = akm09918c_emul_backend_get_sample_range,
214 };
215
216 #define AKM09918C_EMUL(n) \
217 const struct akm09918c_emul_cfg akm09918c_emul_cfg_##n; \
218 struct akm09918c_emul_data akm09918c_emul_data_##n; \
219 EMUL_DT_INST_DEFINE(n, akm09918c_emul_init, &akm09918c_emul_data_##n, \
220 &akm09918c_emul_cfg_##n, &akm09918c_emul_api_i2c, \
221 &akm09918c_emul_sensor_driver_api)
222
223 DT_INST_FOREACH_STATUS_OKAY(AKM09918C_EMUL)
224