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 #define DT_DRV_COMPAT invensense_icm42688
10
11 #include <zephyr/drivers/sensor.h>
12 #include <zephyr/drivers/spi.h>
13 #include <zephyr/sys/byteorder.h>
14
15 #include "icm42688.h"
16 #include "icm42688_decoder.h"
17 #include "icm42688_reg.h"
18 #include "icm42688_rtio.h"
19 #include "icm42688_spi.h"
20 #include "icm42688_trigger.h"
21
22 #include <zephyr/logging/log.h>
23 LOG_MODULE_REGISTER(ICM42688, CONFIG_SENSOR_LOG_LEVEL);
24
icm42688_convert_accel(struct sensor_value * val,int16_t raw_val,struct icm42688_cfg * cfg)25 static void icm42688_convert_accel(struct sensor_value *val, int16_t raw_val,
26 struct icm42688_cfg *cfg)
27 {
28 icm42688_accel_ms(cfg, (int32_t)raw_val, &val->val1, &val->val2);
29 }
30
icm42688_convert_gyro(struct sensor_value * val,int16_t raw_val,struct icm42688_cfg * cfg)31 static void icm42688_convert_gyro(struct sensor_value *val, int16_t raw_val,
32 struct icm42688_cfg *cfg)
33 {
34 icm42688_gyro_rads(cfg, (int32_t)raw_val, &val->val1, &val->val2);
35 }
36
icm42688_convert_temp(struct sensor_value * val,int16_t raw_val)37 static inline void icm42688_convert_temp(struct sensor_value *val, int16_t raw_val)
38 {
39 icm42688_temp_c((int32_t)raw_val, &val->val1, &val->val2);
40 }
41
icm42688_channel_parse_readings(enum sensor_channel chan,int16_t readings[7],struct icm42688_cfg * cfg,struct sensor_value * val)42 int icm42688_channel_parse_readings(enum sensor_channel chan, int16_t readings[7],
43 struct icm42688_cfg *cfg, struct sensor_value *val)
44 {
45 switch (chan) {
46 case SENSOR_CHAN_ACCEL_XYZ:
47 icm42688_convert_accel(&val[0], readings[1], cfg);
48 icm42688_convert_accel(&val[1], readings[2], cfg);
49 icm42688_convert_accel(&val[2], readings[3], cfg);
50 break;
51 case SENSOR_CHAN_ACCEL_X:
52 icm42688_convert_accel(val, readings[1], cfg);
53 break;
54 case SENSOR_CHAN_ACCEL_Y:
55 icm42688_convert_accel(val, readings[2], cfg);
56 break;
57 case SENSOR_CHAN_ACCEL_Z:
58 icm42688_convert_accel(val, readings[3], cfg);
59 break;
60 case SENSOR_CHAN_GYRO_XYZ:
61 icm42688_convert_gyro(&val[0], readings[4], cfg);
62 icm42688_convert_gyro(&val[1], readings[5], cfg);
63 icm42688_convert_gyro(&val[2], readings[6], cfg);
64 break;
65 case SENSOR_CHAN_GYRO_X:
66 icm42688_convert_gyro(val, readings[4], cfg);
67 break;
68 case SENSOR_CHAN_GYRO_Y:
69 icm42688_convert_gyro(val, readings[5], cfg);
70 break;
71 case SENSOR_CHAN_GYRO_Z:
72 icm42688_convert_gyro(val, readings[6], cfg);
73 break;
74 case SENSOR_CHAN_DIE_TEMP:
75 icm42688_convert_temp(val, readings[0]);
76 break;
77 default:
78 return -ENOTSUP;
79 }
80
81 return 0;
82 }
83
icm42688_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)84 static int icm42688_channel_get(const struct device *dev, enum sensor_channel chan,
85 struct sensor_value *val)
86 {
87 struct icm42688_dev_data *data = dev->data;
88
89 return icm42688_channel_parse_readings(chan, data->readings, &data->cfg, val);
90 }
91
icm42688_sample_fetch(const struct device * dev,enum sensor_channel chan)92 static int icm42688_sample_fetch(const struct device *dev, enum sensor_channel chan)
93 {
94 uint8_t status;
95 struct icm42688_dev_data *data = dev->data;
96 const struct icm42688_dev_cfg *cfg = dev->config;
97
98 int res = icm42688_spi_read(&cfg->spi, REG_INT_STATUS, &status, 1);
99
100 if (res) {
101 return res;
102 }
103
104 if (!FIELD_GET(BIT_INT_STATUS_DATA_RDY, status)) {
105 return -EBUSY;
106 }
107
108 uint8_t readings[14];
109
110 res = icm42688_read_all(dev, readings);
111
112 if (res) {
113 return res;
114 }
115
116 for (int i = 0; i < 7; i++) {
117 data->readings[i] = sys_le16_to_cpu((readings[i * 2] << 8) | readings[i * 2 + 1]);
118 }
119
120 return 0;
121 }
122
icm42688_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)123 static int icm42688_attr_set(const struct device *dev, enum sensor_channel chan,
124 enum sensor_attribute attr, const struct sensor_value *val)
125 {
126 const struct icm42688_dev_data *data = dev->data;
127 struct icm42688_cfg new_config = data->cfg;
128 int res = 0;
129
130 __ASSERT_NO_MSG(val != NULL);
131
132 switch (chan) {
133 case SENSOR_CHAN_ACCEL_X:
134 case SENSOR_CHAN_ACCEL_Y:
135 case SENSOR_CHAN_ACCEL_Z:
136 case SENSOR_CHAN_ACCEL_XYZ:
137 if (attr == SENSOR_ATTR_SAMPLING_FREQUENCY) {
138 new_config.accel_odr = icm42688_accel_hz_to_reg(val->val1);
139 } else if (attr == SENSOR_ATTR_FULL_SCALE) {
140 new_config.accel_fs = icm42688_accel_fs_to_reg(sensor_ms2_to_g(val));
141 } else {
142 LOG_ERR("Unsupported attribute");
143 res = -ENOTSUP;
144 }
145 break;
146 case SENSOR_CHAN_GYRO_X:
147 case SENSOR_CHAN_GYRO_Y:
148 case SENSOR_CHAN_GYRO_Z:
149 case SENSOR_CHAN_GYRO_XYZ:
150 if (attr == SENSOR_ATTR_SAMPLING_FREQUENCY) {
151 new_config.gyro_odr = icm42688_gyro_odr_to_reg(val->val1);
152 } else if (attr == SENSOR_ATTR_FULL_SCALE) {
153 new_config.gyro_fs = icm42688_gyro_fs_to_reg(sensor_rad_to_degrees(val));
154 } else {
155 LOG_ERR("Unsupported attribute");
156 res = -EINVAL;
157 }
158 break;
159 case SENSOR_CHAN_ALL:
160 if (attr == SENSOR_ATTR_BATCH_DURATION) {
161 if (val->val1 < 0) {
162 return -EINVAL;
163 }
164 new_config.batch_ticks = val->val1;
165 } else {
166 LOG_ERR("Unsupported attribute");
167 res = -EINVAL;
168 }
169 break;
170 default:
171 LOG_ERR("Unsupported channel");
172 res = -EINVAL;
173 break;
174 }
175
176 if (res) {
177 return res;
178 }
179 return icm42688_safely_configure(dev, &new_config);
180 }
181
icm42688_attr_get(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,struct sensor_value * val)182 static int icm42688_attr_get(const struct device *dev, enum sensor_channel chan,
183 enum sensor_attribute attr, struct sensor_value *val)
184 {
185 const struct icm42688_dev_data *data = dev->data;
186 const struct icm42688_cfg *cfg = &data->cfg;
187 int res = 0;
188
189 __ASSERT_NO_MSG(val != NULL);
190
191 switch (chan) {
192 case SENSOR_CHAN_ACCEL_X:
193 case SENSOR_CHAN_ACCEL_Y:
194 case SENSOR_CHAN_ACCEL_Z:
195 case SENSOR_CHAN_ACCEL_XYZ:
196 if (attr == SENSOR_ATTR_SAMPLING_FREQUENCY) {
197 icm42688_accel_reg_to_hz(cfg->accel_odr, val);
198 } else if (attr == SENSOR_ATTR_FULL_SCALE) {
199 icm42688_accel_reg_to_fs(cfg->accel_fs, val);
200 } else {
201 LOG_ERR("Unsupported attribute");
202 res = -EINVAL;
203 }
204 break;
205 case SENSOR_CHAN_GYRO_X:
206 case SENSOR_CHAN_GYRO_Y:
207 case SENSOR_CHAN_GYRO_Z:
208 case SENSOR_CHAN_GYRO_XYZ:
209 if (attr == SENSOR_ATTR_SAMPLING_FREQUENCY) {
210 icm42688_gyro_reg_to_odr(cfg->gyro_odr, val);
211 } else if (attr == SENSOR_ATTR_FULL_SCALE) {
212 icm42688_gyro_reg_to_fs(cfg->gyro_fs, val);
213 } else {
214 LOG_ERR("Unsupported attribute");
215 res = -EINVAL;
216 }
217 break;
218 case SENSOR_CHAN_ALL:
219 if (attr == SENSOR_ATTR_BATCH_DURATION) {
220 val->val1 = cfg->batch_ticks;
221 val->val2 = 0;
222 } else {
223 LOG_ERR("Unsupported attribute");
224 res = -EINVAL;
225 }
226 break;
227 default:
228 LOG_ERR("Unsupported channel");
229 res = -EINVAL;
230 break;
231 }
232
233 return res;
234 }
235
236 static const struct sensor_driver_api icm42688_driver_api = {
237 .sample_fetch = icm42688_sample_fetch,
238 .channel_get = icm42688_channel_get,
239 .attr_set = icm42688_attr_set,
240 .attr_get = icm42688_attr_get,
241 #ifdef CONFIG_ICM42688_TRIGGER
242 .trigger_set = icm42688_trigger_set,
243 #endif
244 .get_decoder = icm42688_get_decoder,
245 #ifdef CONFIG_SENSOR_ASYNC_API
246 .submit = icm42688_submit,
247 #endif
248 };
249
icm42688_init(const struct device * dev)250 int icm42688_init(const struct device *dev)
251 {
252 struct icm42688_dev_data *data = dev->data;
253 const struct icm42688_dev_cfg *cfg = dev->config;
254 int res;
255
256 if (!spi_is_ready_dt(&cfg->spi)) {
257 LOG_ERR("SPI bus is not ready");
258 return -ENODEV;
259 }
260
261 if (icm42688_reset(dev)) {
262 LOG_ERR("could not initialize sensor");
263 return -EIO;
264 }
265
266 #ifdef CONFIG_ICM42688_TRIGGER
267 res = icm42688_trigger_init(dev);
268 if (res != 0) {
269 LOG_ERR("Failed to initialize triggers");
270 return res;
271 }
272 #endif
273
274 data->cfg.accel_mode = ICM42688_ACCEL_LN;
275 data->cfg.accel_fs = ICM42688_ACCEL_FS_2G;
276 data->cfg.accel_odr = ICM42688_ACCEL_ODR_1000;
277 data->cfg.gyro_mode = ICM42688_GYRO_LN;
278 data->cfg.gyro_fs = ICM42688_GYRO_FS_125;
279 data->cfg.gyro_odr = ICM42688_GYRO_ODR_1000;
280 data->cfg.temp_dis = false;
281 data->cfg.fifo_en = IS_ENABLED(CONFIG_ICM42688_STREAM);
282 data->cfg.batch_ticks = 0;
283 data->cfg.fifo_hires = 0;
284 data->cfg.interrupt1_drdy = 0;
285 data->cfg.interrupt1_fifo_ths = 0;
286 data->cfg.interrupt1_fifo_full = 0;
287
288 res = icm42688_configure(dev, &data->cfg);
289 if (res != 0) {
290 LOG_ERR("Failed to configure");
291 return res;
292 }
293
294 return 0;
295 }
296
297 #ifndef CONFIG_ICM42688_TRIGGER
icm42688_lock(const struct device * dev)298 void icm42688_lock(const struct device *dev)
299 {
300 ARG_UNUSED(dev);
301 }
icm42688_unlock(const struct device * dev)302 void icm42688_unlock(const struct device *dev)
303 {
304 ARG_UNUSED(dev);
305 }
306 #endif
307
308 /* device defaults to spi mode 0/3 support */
309 #define ICM42688_SPI_CFG \
310 SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA | SPI_WORD_SET(8) | SPI_TRANSFER_MSB
311
312 #define ICM42688_RTIO_DEFINE(inst) \
313 SPI_DT_IODEV_DEFINE(icm42688_spi_iodev_##inst, DT_DRV_INST(inst), ICM42688_SPI_CFG, 0U); \
314 RTIO_DEFINE(icm42688_rtio_##inst, 8, 4);
315
316 #define ICM42688_DEFINE_DATA(inst) \
317 IF_ENABLED(CONFIG_ICM42688_STREAM, (ICM42688_RTIO_DEFINE(inst))); \
318 static struct icm42688_dev_data icm42688_driver_##inst = { \
319 IF_ENABLED(CONFIG_ICM42688_STREAM, (.r = &icm42688_rtio_##inst, \
320 .spi_iodev = &icm42688_spi_iodev_##inst,))};
321
322 #define ICM42688_INIT(inst) \
323 ICM42688_DEFINE_DATA(inst); \
324 \
325 static const struct icm42688_dev_cfg icm42688_cfg_##inst = { \
326 .spi = SPI_DT_SPEC_INST_GET(inst, ICM42688_SPI_CFG, 0U), \
327 .gpio_int1 = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, {0}), \
328 }; \
329 \
330 SENSOR_DEVICE_DT_INST_DEFINE(inst, icm42688_init, NULL, &icm42688_driver_##inst, \
331 &icm42688_cfg_##inst, POST_KERNEL, \
332 CONFIG_SENSOR_INIT_PRIORITY, &icm42688_driver_api);
333
334 DT_INST_FOREACH_STATUS_OKAY(ICM42688_INIT)
335