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