1 /*
2 * Copyright (c) 2022 Intel Corporation
3 * Copyright (c) 2022 Esco Medical ApS
4 * Copyright (c) 2020 TDK Invensense
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include <zephyr/drivers/sensor.h>
10 #include <zephyr/drivers/spi.h>
11 #include <zephyr/sys/byteorder.h>
12 #include "icm42688.h"
13 #include "icm42688_reg.h"
14 #include "icm42688_spi.h"
15 #include "icm42688_trigger.h"
16
17 #include <zephyr/logging/log.h>
18 LOG_MODULE_REGISTER(ICM42688_LL, CONFIG_SENSOR_LOG_LEVEL);
19
icm42688_reset(const struct device * dev)20 int icm42688_reset(const struct device *dev)
21 {
22 int res;
23 uint8_t value;
24 const struct icm42688_dev_cfg *dev_cfg = dev->config;
25
26 /* start up time for register read/write after POR is 1ms and supply ramp time is 3ms */
27 k_msleep(3);
28
29 /* perform a soft reset to ensure a clean slate, reset bit will auto-clear */
30 res = icm42688_spi_single_write(&dev_cfg->spi, REG_DEVICE_CONFIG, BIT_SOFT_RESET);
31
32 if (res) {
33 LOG_ERR("write REG_SIGNAL_PATH_RESET failed");
34 return res;
35 }
36
37 /* wait for soft reset to take effect */
38 k_msleep(SOFT_RESET_TIME_MS);
39
40 /* clear reset done int flag */
41 res = icm42688_spi_read(&dev_cfg->spi, REG_INT_STATUS, &value, 1);
42
43 if (res) {
44 return res;
45 }
46
47 if (FIELD_GET(BIT_INT_STATUS_RESET_DONE, value) != 1) {
48 LOG_ERR("unexpected RESET_DONE value, %i", value);
49 return -EINVAL;
50 }
51
52 res = icm42688_spi_read(&dev_cfg->spi, REG_WHO_AM_I, &value, 1);
53 if (res) {
54 return res;
55 }
56
57 if (value != WHO_AM_I_ICM42688) {
58 LOG_ERR("invalid WHO_AM_I value, was %i but expected %i", value, WHO_AM_I_ICM42688);
59 return -EINVAL;
60 }
61
62 return 0;
63 }
64
icm42688_compute_fifo_wm(const struct icm42688_cfg * cfg)65 static uint16_t icm42688_compute_fifo_wm(const struct icm42688_cfg *cfg)
66 {
67 const bool accel_enabled = cfg->accel_pwr_mode != ICM42688_DT_ACCEL_OFF;
68 const bool gyro_enabled = cfg->gyro_pwr_mode != ICM42688_DT_GYRO_OFF;
69 const int pkt_size = cfg->fifo_hires ? 20 : (accel_enabled && gyro_enabled ? 16 : 8);
70 int accel_modr = 0;
71 int gyro_modr = 0;
72 int64_t modr;
73
74 if (cfg->batch_ticks == 0 || (!accel_enabled && !gyro_enabled)) {
75 return 0;
76 }
77
78 if (accel_enabled) {
79 struct sensor_value val = {0};
80
81 icm42688_accel_reg_to_hz(cfg->accel_odr, &val);
82 accel_modr = sensor_value_to_micro(&val) / 1000;
83 }
84 if (gyro_enabled) {
85 struct sensor_value val = {0};
86
87 icm42688_gyro_reg_to_odr(cfg->gyro_odr, &val);
88 gyro_modr = sensor_value_to_micro(&val) / 1000;
89 }
90
91 if (accel_modr == 0) {
92 modr = gyro_modr;
93 } else if (gyro_modr == 0) {
94 modr = accel_modr;
95 } else {
96 /* Need to find the least common multiplier (LCM) */
97 int n1 = accel_modr;
98 int n2 = gyro_modr;
99
100 while (n1 != n2) {
101 if (n1 > n2) {
102 n1 -= n2;
103 } else {
104 n2 -= n1;
105 }
106 }
107 LOG_DBG("GCD=%d", n1);
108 modr = ((int64_t)accel_modr * (int64_t)gyro_modr) / n1;
109 }
110 /* At this point we have 'modr' as mHz which is 1 / msec. */
111
112 /* Convert 'modr' to bytes * batch_ticks / msec */
113 modr *= (int64_t)cfg->batch_ticks * pkt_size;
114
115 /* 'modr' = byte_ticks_per_msec / kticks_per_sec */
116 modr = DIV_ROUND_UP(modr, CONFIG_SYS_CLOCK_TICKS_PER_SEC * INT64_C(1000));
117
118 return (uint16_t)MIN(modr, 0x7ff);
119 }
120
icm42688_configure(const struct device * dev,struct icm42688_cfg * cfg)121 int icm42688_configure(const struct device *dev, struct icm42688_cfg *cfg)
122 {
123 struct icm42688_dev_data *dev_data = dev->data;
124 const struct icm42688_dev_cfg *dev_cfg = dev->config;
125 int res;
126
127 /* Disable interrupts, reconfigured at end */
128 res = icm42688_spi_single_write(&dev_cfg->spi, REG_INT_SOURCE0, 0);
129
130 /* if fifo is enabled right now, disable and flush */
131 if (dev_data->cfg.fifo_en) {
132 res = icm42688_spi_single_write(&dev_cfg->spi, REG_FIFO_CONFIG,
133 FIELD_PREP(MASK_FIFO_MODE, BIT_FIFO_MODE_BYPASS));
134
135 if (res != 0) {
136 LOG_ERR("Error writing FIFO_CONFIG");
137 return -EINVAL;
138 }
139
140 res = icm42688_spi_single_write(&dev_cfg->spi, REG_SIGNAL_PATH_RESET,
141 FIELD_PREP(BIT_FIFO_FLUSH, 1));
142
143 if (res != 0) {
144 LOG_ERR("Error flushing fifo");
145 return -EINVAL;
146 }
147 }
148
149 /* TODO maybe do the next few steps intelligently by checking current config */
150
151 /* Power management to set gyro/accel modes */
152 uint8_t pwr_mgmt0 = FIELD_PREP(MASK_GYRO_MODE, cfg->gyro_pwr_mode) |
153 FIELD_PREP(MASK_ACCEL_MODE, cfg->accel_pwr_mode) |
154 FIELD_PREP(BIT_TEMP_DIS, cfg->temp_dis);
155
156 LOG_DBG("PWR_MGMT0 (0x%x) 0x%x", REG_PWR_MGMT0, pwr_mgmt0);
157 res = icm42688_spi_single_write(&dev_cfg->spi, REG_PWR_MGMT0, pwr_mgmt0);
158
159 if (res != 0) {
160 LOG_ERR("Error writing PWR_MGMT0");
161 return -EINVAL;
162 }
163
164 /* Need to wait at least 200us before updating more registers
165 * see datasheet 14.36
166 */
167 k_busy_wait(250);
168
169 uint8_t accel_config0 = FIELD_PREP(MASK_ACCEL_ODR, cfg->accel_odr) |
170 FIELD_PREP(MASK_ACCEL_UI_FS_SEL, cfg->accel_fs);
171
172 LOG_DBG("ACCEL_CONFIG0 (0x%x) 0x%x", REG_ACCEL_CONFIG0, accel_config0);
173 res = icm42688_spi_single_write(&dev_cfg->spi, REG_ACCEL_CONFIG0, accel_config0);
174 if (res != 0) {
175 LOG_ERR("Error writing ACCEL_CONFIG0");
176 return -EINVAL;
177 }
178
179 uint8_t gyro_config0 = FIELD_PREP(MASK_GYRO_ODR, cfg->gyro_odr) |
180 FIELD_PREP(MASK_GYRO_UI_FS_SEL, cfg->gyro_fs);
181
182 LOG_DBG("GYRO_CONFIG0 (0x%x) 0x%x", REG_GYRO_CONFIG0, gyro_config0);
183 res = icm42688_spi_single_write(&dev_cfg->spi, REG_GYRO_CONFIG0, gyro_config0);
184 if (res != 0) {
185 LOG_ERR("Error writing GYRO_CONFIG0");
186 return -EINVAL;
187 }
188
189 /*
190 * Accelerometer sensor need at least 10ms startup time
191 * Gyroscope sensor need at least 30ms startup time
192 */
193 k_msleep(50);
194
195 /* Ensure FIFO is in bypass mode */
196 uint8_t fifo_config_bypass = FIELD_PREP(MASK_FIFO_MODE, BIT_FIFO_MODE_BYPASS);
197
198 LOG_DBG("FIFO_CONFIG (0x%x) 0x%x", REG_FIFO_CONFIG, fifo_config_bypass);
199 res = icm42688_spi_single_write(&dev_cfg->spi, REG_FIFO_CONFIG, fifo_config_bypass);
200 if (res != 0) {
201 LOG_ERR("Error writing FIFO_CONFIG");
202 return -EINVAL;
203 }
204
205 /* Disable FSYNC */
206 uint8_t tmst_config;
207
208 res = icm42688_spi_single_write(&dev_cfg->spi, REG_FSYNC_CONFIG, 0);
209 if (res != 0) {
210 LOG_ERR("Error writing FSYNC_CONFIG");
211 return -EINVAL;
212 }
213 res = icm42688_spi_read(&dev_cfg->spi, REG_TMST_CONFIG, &tmst_config, 1);
214 if (res != 0) {
215 LOG_ERR("Error reading TMST_CONFIG");
216 return -EINVAL;
217 }
218 res = icm42688_spi_single_write(&dev_cfg->spi, REG_TMST_CONFIG, tmst_config & ~BIT(1));
219 if (res != 0) {
220 LOG_ERR("Error writing TMST_CONFIG");
221 return -EINVAL;
222 }
223
224 /* Pulse mode with async reset (resets interrupt line on int status read) */
225 if (IS_ENABLED(CONFIG_ICM42688_TRIGGER)) {
226 res = icm42688_trigger_enable_interrupt(dev, cfg);
227 } else {
228 res = icm42688_spi_single_write(&dev_cfg->spi, REG_INT_CONFIG,
229 BIT_INT1_DRIVE_CIRCUIT | BIT_INT1_POLARITY);
230 }
231 if (res) {
232 LOG_ERR("Error writing to INT_CONFIG");
233 return res;
234 }
235
236 uint8_t int_config1 = 0;
237
238 if ((cfg->accel_odr <= ICM42688_DT_ACCEL_ODR_4000 ||
239 cfg->gyro_odr <= ICM42688_DT_GYRO_ODR_4000)) {
240 int_config1 = FIELD_PREP(BIT_INT_TPULSE_DURATION, 1) |
241 FIELD_PREP(BIT_INT_TDEASSERT_DISABLE, 1);
242 }
243
244 res = icm42688_spi_single_write(&dev_cfg->spi, REG_INT_CONFIG1, int_config1);
245 if (res) {
246 LOG_ERR("Error writing to INT_CONFIG1");
247 return res;
248 }
249
250 /* fifo configuration steps if desired */
251 if (cfg->fifo_en) {
252 LOG_INF("FIFO ENABLED");
253
254 /* Setup desired FIFO packet fields, maybe should base this on the other
255 * temp/accel/gyro en fields in cfg
256 */
257 uint8_t fifo_cfg1 =
258 FIELD_PREP(BIT_FIFO_TEMP_EN, 1) | FIELD_PREP(BIT_FIFO_GYRO_EN, 1) |
259 FIELD_PREP(BIT_FIFO_ACCEL_EN, 1) | FIELD_PREP(BIT_FIFO_TMST_FSYNC_EN, 1);
260
261 LOG_DBG("FIFO_CONFIG1 (0x%x) 0x%x", REG_FIFO_CONFIG1, fifo_cfg1);
262 res = icm42688_spi_single_write(&dev_cfg->spi, REG_FIFO_CONFIG1, fifo_cfg1);
263 if (res != 0) {
264 LOG_ERR("Error writing FIFO_CONFIG1");
265 return -EINVAL;
266 }
267
268 /* Set watermark and interrupt handling first */
269 uint16_t fifo_wm = icm42688_compute_fifo_wm(cfg);
270 uint8_t fifo_wml = fifo_wm & 0xFF;
271
272 LOG_DBG("FIFO_CONFIG2( (0x%x)) (WM Low) 0x%x", REG_FIFO_CONFIG2, fifo_wml);
273 res = icm42688_spi_single_write(&dev_cfg->spi, REG_FIFO_CONFIG2, fifo_wml);
274 if (res != 0) {
275 LOG_ERR("Error writing FIFO_CONFIG2");
276 return -EINVAL;
277 }
278
279 uint8_t fifo_wmh = (fifo_wm >> 8) & 0x0F;
280
281 LOG_DBG("FIFO_CONFIG3 (0x%x) (WM High) 0x%x", REG_FIFO_CONFIG3, fifo_wmh);
282 res = icm42688_spi_single_write(&dev_cfg->spi, REG_FIFO_CONFIG3, fifo_wmh);
283 if (res != 0) {
284 LOG_ERR("Error writing FIFO_CONFIG3");
285 return -EINVAL;
286 }
287
288 /* Begin streaming */
289 uint8_t fifo_config = FIELD_PREP(MASK_FIFO_MODE, BIT_FIFO_MODE_STREAM);
290
291 LOG_DBG("FIFO_CONFIG (0x%x) 0x%x", REG_FIFO_CONFIG, 1 << 6);
292 res = icm42688_spi_single_write(&dev_cfg->spi, REG_FIFO_CONFIG, fifo_config);
293
294 /* Config interrupt source to only be fifo wm/full */
295 uint8_t int_source0 = BIT_FIFO_FULL_INT1_EN | BIT_FIFO_THS_INT1_EN;
296
297 LOG_DBG("INT_SOURCE0 (0x%x) 0x%x", REG_INT_SOURCE0, int_source0);
298 res = icm42688_spi_single_write(&dev_cfg->spi, REG_INT_SOURCE0, int_source0);
299 if (res) {
300 return res;
301 }
302 } else {
303 LOG_INF("FIFO DISABLED");
304
305 /* No fifo mode so set data ready as interrupt source */
306 uint8_t int_source0 = BIT_UI_DRDY_INT1_EN;
307
308 LOG_DBG("INT_SOURCE0 (0x%x) 0x%x", REG_INT_SOURCE0, int_source0);
309 res = icm42688_spi_single_write(&dev_cfg->spi, REG_INT_SOURCE0, int_source0);
310 if (res) {
311 return res;
312 }
313 }
314
315 return res;
316 }
317
icm42688_safely_configure(const struct device * dev,struct icm42688_cfg * cfg)318 int icm42688_safely_configure(const struct device *dev, struct icm42688_cfg *cfg)
319 {
320 struct icm42688_dev_data *drv_data = dev->data;
321 int ret = icm42688_configure(dev, cfg);
322
323 if (ret == 0) {
324 drv_data->cfg = *cfg;
325 } else {
326 ret = icm42688_configure(dev, &drv_data->cfg);
327 }
328
329 return ret;
330 }
331
icm42688_read_all(const struct device * dev,uint8_t data[14])332 int icm42688_read_all(const struct device *dev, uint8_t data[14])
333 {
334 const struct icm42688_dev_cfg *dev_cfg = dev->config;
335 int res;
336
337 res = icm42688_spi_read(&dev_cfg->spi, REG_TEMP_DATA1, data, 14);
338
339 return res;
340 }
341