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