1 /*
2 * Copyright (c) 2022 Intel Corporation
3 * Copyright (c) 2025 Croxel Inc.
4 * Copyright (c) 2025 CogniPilot Foundation
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #ifndef ZEPHYR_DRIVERS_SENSOR_ICM45686_H_
10 #define ZEPHYR_DRIVERS_SENSOR_ICM45686_H_
11
12 #include <stdint.h>
13 #include <zephyr/drivers/sensor.h>
14 #include <zephyr/drivers/gpio.h>
15 #include <zephyr/dt-bindings/sensor/icm45686.h>
16 #include <zephyr/rtio/rtio.h>
17
18 struct icm45686_encoded_payload {
19 union {
20 uint8_t buf[14];
21 int16_t readings[7];
22 struct {
23 struct {
24 int16_t x;
25 int16_t y;
26 int16_t z;
27 } accel;
28 struct {
29 int16_t x;
30 int16_t y;
31 int16_t z;
32 } gyro;
33 int16_t temp;
34 } __attribute__((__packed__));
35 };
36 };
37
38 struct icm45686_encoded_fifo_payload {
39 union {
40 uint8_t buf[20];
41 struct {
42 uint8_t header;
43 struct {
44 int16_t x;
45 int16_t y;
46 int16_t z;
47 } accel;
48 struct {
49 int16_t x;
50 int16_t y;
51 int16_t z;
52 } gyro;
53 int16_t temp;
54 uint16_t timestamp;
55 struct {
56 uint8_t gyro_x : 4;
57 uint8_t accel_x : 4;
58 uint8_t gyro_y : 4;
59 uint8_t accel_y : 4;
60 uint8_t gyro_z : 4;
61 uint8_t accel_z : 4;
62 } lsb;
63 } __attribute__((__packed__));
64 };
65 };
66
67 struct icm45686_encoded_header {
68 uint64_t timestamp;
69 uint8_t accel_fs : 4;
70 uint8_t gyro_fs : 4;
71 uint8_t events : 3;
72 uint8_t channels : 7;
73 uint16_t fifo_count;
74 };
75
76 struct icm45686_encoded_data {
77 struct icm45686_encoded_header header;
78 union {
79 struct icm45686_encoded_payload payload;
80 struct icm45686_encoded_fifo_payload fifo_payload;
81 };
82 };
83
84 struct icm45686_triggers {
85 struct gpio_callback cb;
86 const struct device *dev;
87 struct k_mutex lock;
88 struct {
89 struct sensor_trigger trigger;
90 sensor_trigger_handler_t handler;
91 } entry;
92 #if defined(CONFIG_ICM45686_TRIGGER_OWN_THREAD)
93 K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_ICM45686_THREAD_STACK_SIZE);
94 struct k_thread thread;
95 struct k_sem sem;
96 #elif defined(CONFIG_ICM45686_TRIGGER_GLOBAL_THREAD)
97 struct k_work work;
98 #endif
99 };
100
101 struct icm45686_stream {
102 struct gpio_callback cb;
103 const struct device *dev;
104 struct rtio_iodev_sqe *iodev_sqe;
105 atomic_t in_progress;
106 struct {
107 struct {
108 bool drdy : 1;
109 bool fifo_ths : 1;
110 bool fifo_full : 1;
111 } enabled;
112 struct {
113 enum sensor_stream_data_opt drdy;
114 enum sensor_stream_data_opt fifo_ths;
115 enum sensor_stream_data_opt fifo_full;
116 } opt;
117 bool drdy : 1;
118 bool fifo_ths : 1;
119 bool fifo_full : 1;
120 } settings;
121 struct {
122 uint64_t timestamp;
123 uint8_t int_status;
124 uint16_t fifo_count;
125 struct {
126 bool drdy : 1;
127 bool fifo_ths : 1;
128 bool fifo_full : 1;
129 } events;
130 } data;
131 };
132
133 struct icm45686_data {
134 struct {
135 struct rtio_iodev *iodev;
136 struct rtio *ctx;
137 } rtio;
138 /** Single-shot encoded data instance to support fetch/get API */
139 struct icm45686_encoded_data edata;
140 #if defined(CONFIG_ICM45686_TRIGGER)
141 struct icm45686_triggers triggers;
142 #elif defined(CONFIG_ICM45686_STREAM)
143 struct icm45686_stream stream;
144 #endif /* CONFIG_ICM45686_TRIGGER */
145 };
146
147 struct icm45686_config {
148 struct {
149 struct {
150 uint8_t pwr_mode : 2;
151 uint8_t fs : 4;
152 uint8_t odr : 4;
153 uint8_t lpf : 3;
154 } accel;
155 struct {
156 uint8_t pwr_mode : 2;
157 uint8_t fs : 4;
158 uint8_t odr : 4;
159 uint8_t lpf : 3;
160 } gyro;
161 uint16_t fifo_watermark;
162 } settings;
163 struct gpio_dt_spec int_gpio;
164 };
165
icm45686_accel_ms(uint8_t fs,int32_t in,bool high_res,int32_t * out_ms,int32_t * out_ums)166 static inline void icm45686_accel_ms(uint8_t fs,
167 int32_t in,
168 bool high_res,
169 int32_t *out_ms,
170 int32_t *out_ums)
171 {
172 int64_t sensitivity;
173 uint64_t full_scale_range_lsb = high_res ? 524288 : 32768;
174
175 switch (fs) {
176 case ICM45686_DT_ACCEL_FS_32:
177 sensitivity = full_scale_range_lsb / 32;
178 break;
179 case ICM45686_DT_ACCEL_FS_16:
180 sensitivity = full_scale_range_lsb / 16;
181 break;
182 case ICM45686_DT_ACCEL_FS_8:
183 sensitivity = full_scale_range_lsb / 8;
184 break;
185 case ICM45686_DT_ACCEL_FS_4:
186 sensitivity = full_scale_range_lsb / 4;
187 break;
188 case ICM45686_DT_ACCEL_FS_2:
189 sensitivity = full_scale_range_lsb / 2;
190 break;
191 default:
192 CODE_UNREACHABLE;
193 }
194
195 /* Convert to micrometers/s^2 */
196 int64_t in_ms = in * SENSOR_G;
197
198 /* meters/s^2 whole values */
199 *out_ms = in_ms / (sensitivity * 1000000LL);
200
201 /* micrometers/s^2 */
202 *out_ums = (in_ms - (*out_ms * sensitivity * 1000000LL)) / sensitivity;
203 }
204
icm45686_gyro_rads(uint8_t fs,int32_t in,bool high_res,int32_t * out_rads,int32_t * out_urads)205 static inline void icm45686_gyro_rads(uint8_t fs,
206 int32_t in,
207 bool high_res,
208 int32_t *out_rads,
209 int32_t *out_urads)
210 {
211 int64_t sensitivity_x10;
212 uint64_t full_scale_range_lsb = high_res ? 524288 : 32768;
213
214 switch (fs) {
215 case ICM45686_DT_GYRO_FS_4000:
216 sensitivity_x10 = full_scale_range_lsb * 10 / 4000;
217 break;
218 case ICM45686_DT_GYRO_FS_2000:
219 sensitivity_x10 = full_scale_range_lsb * 10 / 2000;
220 break;
221 case ICM45686_DT_GYRO_FS_1000:
222 sensitivity_x10 = full_scale_range_lsb * 10 / 1000;
223 break;
224 case ICM45686_DT_GYRO_FS_500:
225 sensitivity_x10 = full_scale_range_lsb * 10 / 500;
226 break;
227 case ICM45686_DT_GYRO_FS_250:
228 sensitivity_x10 = full_scale_range_lsb * 10 / 250;
229 break;
230 case ICM45686_DT_GYRO_FS_125:
231 sensitivity_x10 = full_scale_range_lsb * 10 / 125;
232 break;
233 case ICM45686_DT_GYRO_FS_62_5:
234 sensitivity_x10 = full_scale_range_lsb * 10 * 10 / 625;
235 break;
236 case ICM45686_DT_GYRO_FS_31_25:
237 sensitivity_x10 = full_scale_range_lsb * 10 * 100 / 3125;
238 break;
239 case ICM45686_DT_GYRO_FS_15_625:
240 sensitivity_x10 = full_scale_range_lsb * 10 * 1000 / 15625;
241 break;
242 default:
243 CODE_UNREACHABLE;
244 }
245
246 int64_t in10_rads = (int64_t)in * SENSOR_PI * 10LL;
247
248 /* Whole rad/s */
249 *out_rads = in10_rads / (sensitivity_x10 * 180LL * 1000000LL);
250
251 /* microrad/s */
252 *out_urads = (in10_rads - (*out_rads * sensitivity_x10 * 180LL * 1000000LL)) /
253 (sensitivity_x10 * 180LL);
254 }
255
icm45686_temp_c(int32_t in,int32_t * out_c,uint32_t * out_uc)256 static inline void icm45686_temp_c(int32_t in, int32_t *out_c, uint32_t *out_uc)
257 {
258 int64_t sensitivity = 13248; /* value equivalent for x100 1c */
259
260 /* Offset by 25 degrees Celsius */
261 int64_t in100 = (in * 100) + (25 * sensitivity);
262
263 /* Whole celsius */
264 *out_c = in100 / sensitivity;
265
266 /* Micro celsius */
267 *out_uc = ((in100 - (*out_c) * sensitivity) * INT64_C(1000000)) / sensitivity;
268 }
269
270 #endif /* ZEPHYR_DRIVERS_SENSOR_ICM45686_H_ */
271