Skip to content

Commit f838c1f

Browse files
committed
sensor: icm45686: Add Triggers functionality
Only working with SENSOR_TRIG_DATA_READY so far. Signed-off-by: Luis Ubieda <[email protected]>
1 parent 35f49ba commit f838c1f

File tree

7 files changed

+335
-1
lines changed

7 files changed

+335
-1
lines changed

drivers/sensor/tdk/icm45686/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@ zephyr_library_sources(
99
zephyr_library_sources_ifdef(CONFIG_SENSOR_ASYNC_API
1010
icm45686_decoder.c
1111
)
12+
zephyr_library_sources_ifdef(CONFIG_ICM45686_TRIGGER
13+
icm45686_trigger.c
14+
)
Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Copyright (c) 2025 Croxel Inc.
22
# SPDX-License-Identifier: Apache-2.0
33

4-
config ICM45686
4+
menuconfig ICM45686
55
bool "ICM45686 High-precision 6-Axis Motion Tracking Device"
66
default y
77
depends on DT_HAS_INVENSENSE_ICM45686_ENABLED
@@ -10,3 +10,47 @@ config ICM45686
1010
help
1111
Enable driver for ICM45686 High-precision 6-axis motion
1212
tracking device.
13+
14+
if ICM45686
15+
16+
choice
17+
prompt "Trigger mode"
18+
default ICM45686_TRIGGER_GLOBAL_THREAD
19+
help
20+
Specify the type of triggering to be used by the driver
21+
22+
config ICM45686_TRIGGER_NONE
23+
bool "No trigger"
24+
25+
config ICM45686_TRIGGER_GLOBAL_THREAD
26+
bool "Use global thread"
27+
depends on GPIO
28+
depends on $(dt_compat_any_has_prop,$(DT_COMPAT_INVENSENSE_ICM45686),int-gpios)
29+
select ICM45686_TRIGGER
30+
31+
config ICM45686_TRIGGER_OWN_THREAD
32+
bool "Use own thread"
33+
depends on GPIO
34+
depends on $(dt_compat_any_has_prop,$(DT_COMPAT_INVENSENSE_ICM45686),int-gpios)
35+
select ICM45686_TRIGGER
36+
37+
endchoice
38+
39+
config ICM45686_TRIGGER
40+
bool
41+
42+
config ICM45686_THREAD_PRIORITY
43+
int "Own thread priority"
44+
depends on ICM45686_TRIGGER_OWN_THREAD
45+
default 10
46+
help
47+
The priority of the thread used for handling triggers.
48+
49+
config ICM45686_THREAD_STACK_SIZE
50+
int "Own thread stack size"
51+
depends on ICM45686_TRIGGER_OWN_THREAD
52+
default 1024
53+
help
54+
The thread stack size.
55+
56+
endif # ICM45686

drivers/sensor/tdk/icm45686/icm45686.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "icm45686_reg.h"
2121
#include "icm45686_bus.h"
2222
#include "icm45686_decoder.h"
23+
#include "icm45686_trigger.h"
2324

2425
#include <zephyr/logging/log.h>
2526
LOG_MODULE_REGISTER(ICM45686, CONFIG_SENSOR_LOG_LEVEL);
@@ -193,6 +194,9 @@ static void icm45686_submit(const struct device *dev, struct rtio_iodev_sqe *iod
193194
static DEVICE_API(sensor, icm45686_driver_api) = {
194195
.sample_fetch = icm45686_sample_fetch,
195196
.channel_get = icm45686_channel_get,
197+
#if defined(CONFIG_ICM45686_TRIGGER)
198+
.trigger_set = icm45686_trigger_set,
199+
#endif /* CONFIG_ICM45686_TRIGGER */
196200
#if defined(CONFIG_SENSOR_ASYNC_API)
197201
.get_decoder = icm45686_get_decoder,
198202
.submit = icm45686_submit,
@@ -281,6 +285,14 @@ static int icm45686_init(const struct device *dev)
281285
return err;
282286
}
283287

288+
if (IS_ENABLED(CONFIG_ICM45686_TRIGGER)) {
289+
err = icm45686_trigger_init(dev);
290+
if (err) {
291+
LOG_ERR("Failed to initialize triggers: %d", err);
292+
return err;
293+
}
294+
}
295+
284296
LOG_DBG("Init OK");
285297

286298
return 0;
@@ -317,6 +329,7 @@ static int icm45686_init(const struct device *dev)
317329
.odr = DT_INST_PROP(inst, gyro_odr), \
318330
}, \
319331
}, \
332+
.int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, {0}), \
320333
}; \
321334
static struct icm45686_data icm45686_data_##inst = { \
322335
.edata.header = { \

drivers/sensor/tdk/icm45686/icm45686.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,33 @@ struct icm45686_encoded_data {
4747
struct icm45686_encoded_payload payload;
4848
};
4949

50+
struct icm45686_triggers {
51+
struct gpio_callback cb;
52+
const struct device *dev;
53+
struct k_mutex lock;
54+
struct {
55+
struct sensor_trigger trigger;
56+
sensor_trigger_handler_t handler;
57+
} entry;
58+
#if defined(CONFIG_ICM45686_TRIGGER_OWN_THREAD)
59+
K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_ICM45686_THREAD_STACK_SIZE);
60+
struct k_thread thread;
61+
struct k_sem sem;
62+
#elif defined(CONFIG_ICM45686_TRIGGER_GLOBAL_THREAD)
63+
struct k_work work;
64+
#endif
65+
};
66+
5067
struct icm45686_data {
5168
struct {
5269
struct rtio_iodev *iodev;
5370
struct rtio *ctx;
5471
} rtio;
5572
/** Single-shot encoded data instance to support fetch/get API */
5673
struct icm45686_encoded_data edata;
74+
#if defined(CONFIG_ICM45686_TRIGGER)
75+
struct icm45686_triggers triggers;
76+
#endif /* CONFIG_ICM45686_TRIGGER */
5777
};
5878

5979
struct icm45686_config {
@@ -69,6 +89,7 @@ struct icm45686_config {
6989
uint8_t odr : 4;
7090
} gyro;
7191
} settings;
92+
struct gpio_dt_spec int_gpio;
7293
};
7394

7495
static inline void icm45686_accel_ms(struct icm45686_encoded_data *edata,

drivers/sensor/tdk/icm45686/icm45686_reg.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@
2929
#define REG_TEMP_DATA1_UI 0x0C
3030
#define REG_TEMP_DATA0_UI 0x0D
3131
#define REG_PWR_MGMT0 0x10
32+
#define REG_INT1_CONFIG0 0x16
33+
#define REG_INT1_CONFIG1 0x17
34+
#define REG_INT1_CONFIG2 0x18
35+
#define REG_INT1_STATUS0 0x19
36+
#define REG_INT1_STATUS1 0x1A
3237
#define REG_ACCEL_CONFIG0 0x1B
3338
#define REG_GYRO_CONFIG0 0x1C
3439
#define REG_DRIVE_CONFIG0 0x32
@@ -49,6 +54,18 @@
4954

5055
#define REG_MISC2_SOFT_RST(val) ((val << 1) & BIT(1))
5156

57+
#define REG_INT1_CONFIG0_STATUS_EN_DRDY(val) (((val) & BIT_MASK(1)) << 2)
58+
#define REG_INT1_CONFIG0_STATUS_EN_FIFO_THS(val) (((val) & BIT_MASK(1)) << 1)
59+
#define REG_INT1_CONFIG0_STATUS_EN_FIFO_FULL(val) ((val) & BIT_MASK(1))
60+
61+
#define REG_INT1_CONFIG2_EN_OPEN_DRAIN(val) (((val) & BIT_MASK(1)) << 2)
62+
#define REG_INT1_CONFIG2_EN_LATCH_MODE(val) (((val) & BIT_MASK(1)) << 1)
63+
#define REG_INT1_CONFIG2_EN_ACTIVE_HIGH(val) ((val) & BIT_MASK(1))
64+
65+
#define REG_INT1_STATUS0_DRDY(val) (((val) & BIT_MASK(1)) << 2)
66+
#define REG_INT1_STATUS0_FIFO_THS(val) (((val) & BIT_MASK(1)) << 1)
67+
#define REG_INT1_STATUS0_FIFO_FULL(val) ((val) & BIT_MASK(1))
68+
5269
/* Misc. Defines */
5370
#define WHO_AM_I_ICM45686 0xE9
5471

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

0 commit comments

Comments
 (0)