1 /*
2  * Copyright (c) 2022 Intel Corporation
3  * Copyright (c) 2023 Google LLC
4  * Copyright (c) 2025 Croxel Inc.
5  * Copyright (c) 2025 CogniPilot Foundation
6  *
7  * SPDX-License-Identifier: Apache-2.0
8  */
9 
10 #include <zephyr/drivers/sensor.h>
11 #include <zephyr/drivers/gpio.h>
12 
13 #include "icm45686.h"
14 #include "icm45686_bus.h"
15 #include "icm45686_trigger.h"
16 
17 #include <zephyr/logging/log.h>
18 LOG_MODULE_REGISTER(ICM45686_TRIGGER, CONFIG_SENSOR_LOG_LEVEL);
19 
icm45686_gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)20 static void icm45686_gpio_callback(const struct device *dev,
21 				   struct gpio_callback *cb,
22 				   uint32_t pins)
23 {
24 	struct icm45686_data *data = CONTAINER_OF(cb,
25 						  struct icm45686_data,
26 						  triggers.cb);
27 
28 	ARG_UNUSED(dev);
29 	ARG_UNUSED(pins);
30 
31 #if defined(CONFIG_ICM45686_TRIGGER_OWN_THREAD)
32 	k_sem_give(&data->triggers.sem);
33 #elif defined(CONFIG_ICM45686_TRIGGER_GLOBAL_THREAD)
34 	k_work_submit(&data->triggers.work);
35 #endif
36 }
37 
icm45686_thread_cb(const struct device * dev)38 static void icm45686_thread_cb(const struct device *dev)
39 {
40 	struct icm45686_data *data = dev->data;
41 
42 	(void)k_mutex_lock(&data->triggers.lock, K_FOREVER);
43 
44 	if (data->triggers.entry.handler) {
45 		data->triggers.entry.handler(dev, &data->triggers.entry.trigger);
46 	}
47 
48 	(void)k_mutex_unlock(&data->triggers.lock);
49 }
50 
51 #if defined(CONFIG_ICM45686_TRIGGER_OWN_THREAD)
52 
icm45686_thread(void * p1,void * p2,void * p3)53 static void icm45686_thread(void *p1, void *p2, void *p3)
54 {
55 	ARG_UNUSED(p2);
56 	ARG_UNUSED(p3);
57 
58 	struct icm45686_data *data = p1;
59 
60 	while (true) {
61 		k_sem_take(&data->triggers.sem, K_FOREVER);
62 
63 		icm45686_thread_cb(data->triggers.dev);
64 	}
65 }
66 
67 #elif defined(CONFIG_ICM45686_TRIGGER_GLOBAL_THREAD)
68 
icm45686_work_handler(struct k_work * work)69 static void icm45686_work_handler(struct k_work *work)
70 {
71 	struct icm45686_data *data = CONTAINER_OF(work,
72 						  struct icm45686_data,
73 						  triggers.work);
74 
75 	icm45686_thread_cb(data->triggers.dev);
76 }
77 
78 #endif
79 
icm45686_enable_drdy(const struct device * dev,bool enable)80 static int icm45686_enable_drdy(const struct device *dev, bool enable)
81 {
82 	uint8_t val;
83 	int err;
84 
85 	err = icm45686_bus_read(dev, REG_INT1_CONFIG0, &val, 1);
86 	if (err) {
87 		return err;
88 	}
89 
90 	val &= ~REG_INT1_CONFIG0_STATUS_EN_DRDY(true);
91 	err = icm45686_bus_write(dev, REG_INT1_CONFIG0, &val, 1);
92 	if (err) {
93 		return err;
94 	}
95 
96 	if (enable) {
97 		val |= REG_INT1_CONFIG0_STATUS_EN_DRDY(true);
98 	}
99 
100 	return icm45686_bus_write(dev, REG_INT1_CONFIG0, &val, 1);
101 }
102 
icm45686_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)103 int icm45686_trigger_set(const struct device *dev,
104 			 const struct sensor_trigger *trig,
105 			 sensor_trigger_handler_t handler)
106 {
107 	int err = 0;
108 	struct icm45686_data *data = dev->data;
109 
110 	(void)k_mutex_lock(&data->triggers.lock, K_FOREVER);
111 
112 	switch (trig->type) {
113 	case SENSOR_TRIG_DATA_READY:
114 		data->triggers.entry.trigger = *trig;
115 		data->triggers.entry.handler = handler;
116 
117 		if (handler) {
118 			/* Enable data ready interrupt */
119 			err = icm45686_enable_drdy(dev, true);
120 		} else {
121 			/* Disable data ready interrupt */
122 			err = icm45686_enable_drdy(dev, false);
123 		}
124 		break;
125 	default:
126 		err = -ENOTSUP;
127 		break;
128 	}
129 
130 	(void)k_mutex_unlock(&data->triggers.lock);
131 
132 	return err;
133 }
134 
icm45686_trigger_init(const struct device * dev)135 int icm45686_trigger_init(const struct device *dev)
136 {
137 	const struct icm45686_config *cfg = dev->config;
138 	struct icm45686_data *data = dev->data;
139 	uint8_t val = 0;
140 	int err;
141 
142 	err = k_mutex_init(&data->triggers.lock);
143 	__ASSERT_NO_MSG(!err);
144 
145 	/** Needed to get back the device handle from the callback context */
146 	data->triggers.dev = dev;
147 
148 #if defined(CONFIG_ICM45686_TRIGGER_OWN_THREAD)
149 
150 	err = k_sem_init(&data->triggers.sem, 0, 1);
151 	__ASSERT_NO_MSG(!err);
152 
153 	(void)k_thread_create(&data->triggers.thread,
154 			      data->triggers.thread_stack,
155 			      K_KERNEL_STACK_SIZEOF(data->triggers.thread_stack),
156 			      icm45686_thread,
157 			      data,
158 			      NULL,
159 			      NULL,
160 			      K_PRIO_COOP(CONFIG_ICM45686_THREAD_PRIORITY),
161 			      0,
162 			      K_NO_WAIT);
163 
164 #elif defined(CONFIG_ICM45686_TRIGGER_GLOBAL_THREAD)
165 	k_work_init(&data->triggers.work, icm45686_work_handler);
166 #endif
167 
168 	if (!cfg->int_gpio.port) {
169 		LOG_ERR("Interrupt GPIO not supplied");
170 		return -ENODEV;
171 	}
172 
173 	if (!gpio_is_ready_dt(&cfg->int_gpio)) {
174 		LOG_ERR("Interrupt GPIO not ready");
175 		return -ENODEV;
176 	}
177 
178 	err = gpio_pin_configure_dt(&cfg->int_gpio, GPIO_INPUT);
179 	if (err) {
180 		LOG_ERR("Failed to configure interrupt GPIO");
181 		return -EIO;
182 	}
183 
184 	gpio_init_callback(&data->triggers.cb,
185 			icm45686_gpio_callback,
186 			BIT(cfg->int_gpio.pin));
187 
188 	err = gpio_add_callback(cfg->int_gpio.port, &data->triggers.cb);
189 	if (err) {
190 		LOG_ERR("Failed to add interrupt callback");
191 		return -EIO;
192 	}
193 
194 	err = gpio_pin_interrupt_configure_dt(&cfg->int_gpio,
195 					      GPIO_INT_EDGE_TO_ACTIVE);
196 	if (err) {
197 		LOG_ERR("Failed to configure interrupt");
198 	}
199 
200 	err = icm45686_bus_write(dev, REG_INT1_CONFIG0, &val, 1);
201 	if (err) {
202 		LOG_ERR("Failed to disable all INTs");
203 	}
204 
205 	val = REG_INT1_CONFIG2_EN_OPEN_DRAIN(false) |
206 	      REG_INT1_CONFIG2_EN_ACTIVE_HIGH(true);
207 
208 	err = icm45686_bus_write(dev, REG_INT1_CONFIG2, &val, 1);
209 	if (err) {
210 		LOG_ERR("Failed to configure INT as push-pull: %d", err);
211 	}
212 
213 	return err;
214 }
215