1 /*
2  * Copyright (c) 2022 Esco Medical ApS
3  * Copyright (c) 2016 TDK Invensense
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <zephyr/device.h>
9 #include <zephyr/drivers/sensor.h>
10 #include <zephyr/sys/util.h>
11 #include "icm42670.h"
12 #include "icm42670_reg.h"
13 #include "icm42670_spi.h"
14 #include "icm42670_trigger.h"
15 
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_DECLARE(ICM42670, CONFIG_SENSOR_LOG_LEVEL);
18 
icm42670_gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)19 static void icm42670_gpio_callback(const struct device *dev, struct gpio_callback *cb,
20 				   uint32_t pins)
21 {
22 	ARG_UNUSED(dev);
23 	ARG_UNUSED(pins);
24 
25 	struct icm42670_data *data = CONTAINER_OF(cb, struct icm42670_data, gpio_cb);
26 
27 #if defined(CONFIG_ICM42670_TRIGGER_OWN_THREAD)
28 	k_sem_give(&data->gpio_sem);
29 #elif defined(CONFIG_ICM42670_TRIGGER_GLOBAL_THREAD)
30 	k_work_submit(&data->work);
31 #endif
32 }
33 
icm42670_thread_cb(const struct device * dev)34 static void icm42670_thread_cb(const struct device *dev)
35 {
36 	struct icm42670_data *data = dev->data;
37 	const struct icm42670_config *cfg = dev->config;
38 
39 	icm42670_lock(dev);
40 	gpio_pin_interrupt_configure_dt(&cfg->gpio_int, GPIO_INT_DISABLE);
41 
42 	if (data->data_ready_handler) {
43 		data->data_ready_handler(dev, data->data_ready_trigger);
44 	}
45 
46 	gpio_pin_interrupt_configure_dt(&cfg->gpio_int, GPIO_INT_EDGE_TO_ACTIVE);
47 	icm42670_unlock(dev);
48 }
49 
50 #if defined(CONFIG_ICM42670_TRIGGER_OWN_THREAD)
51 
icm42670_thread(void * p1,void * p2,void * p3)52 static void icm42670_thread(void *p1, void *p2, void *p3)
53 {
54 	ARG_UNUSED(p2);
55 	ARG_UNUSED(p3);
56 
57 	struct icm42670_data *data = p1;
58 
59 	while (1) {
60 		k_sem_take(&data->gpio_sem, K_FOREVER);
61 		icm42670_thread_cb(data->dev);
62 	}
63 }
64 
65 #elif defined(CONFIG_ICM42670_TRIGGER_GLOBAL_THREAD)
66 
icm42670_work_handler(struct k_work * work)67 static void icm42670_work_handler(struct k_work *work)
68 {
69 	struct icm42670_data *data = CONTAINER_OF(work, struct icm42670_data, work);
70 
71 	icm42670_thread_cb(data->dev);
72 }
73 
74 #endif
75 
icm42670_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)76 int icm42670_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
77 			 sensor_trigger_handler_t handler)
78 {
79 	int res = 0;
80 	struct icm42670_data *data = dev->data;
81 	const struct icm42670_config *cfg = dev->config;
82 
83 	if (!handler) {
84 		return -EINVAL;
85 	}
86 
87 	icm42670_lock(dev);
88 	gpio_pin_interrupt_configure_dt(&cfg->gpio_int, GPIO_INT_DISABLE);
89 
90 	switch (trig->type) {
91 	case SENSOR_TRIG_DATA_READY:
92 		data->data_ready_handler = handler;
93 		data->data_ready_trigger = trig;
94 		break;
95 	default:
96 		res = -ENOTSUP;
97 		break;
98 	}
99 
100 	icm42670_unlock(dev);
101 	gpio_pin_interrupt_configure_dt(&cfg->gpio_int, GPIO_INT_EDGE_TO_ACTIVE);
102 
103 	return res;
104 }
105 
icm42670_trigger_init(const struct device * dev)106 int icm42670_trigger_init(const struct device *dev)
107 {
108 	struct icm42670_data *data = dev->data;
109 	const struct icm42670_config *cfg = dev->config;
110 	int res = 0;
111 
112 	if (!cfg->gpio_int.port) {
113 		LOG_ERR("trigger enabled but no interrupt gpio supplied");
114 		return -ENODEV;
115 	}
116 
117 	if (!device_is_ready(cfg->gpio_int.port)) {
118 		LOG_ERR("gpio_int gpio not ready");
119 		return -ENODEV;
120 	}
121 
122 	data->dev = dev;
123 	gpio_pin_configure_dt(&cfg->gpio_int, GPIO_INPUT);
124 	gpio_init_callback(&data->gpio_cb, icm42670_gpio_callback, BIT(cfg->gpio_int.pin));
125 	res = gpio_add_callback(cfg->gpio_int.port, &data->gpio_cb);
126 
127 	if (res < 0) {
128 		LOG_ERR("Failed to set gpio callback");
129 		return res;
130 	}
131 
132 	k_mutex_init(&data->mutex);
133 
134 #if defined(CONFIG_ICM42670_TRIGGER_OWN_THREAD)
135 	k_sem_init(&data->gpio_sem, 0, K_SEM_MAX_LIMIT);
136 	k_thread_create(&data->thread, data->thread_stack, CONFIG_ICM42670_THREAD_STACK_SIZE,
137 			icm42670_thread, data, NULL, NULL,
138 			K_PRIO_COOP(CONFIG_ICM42670_THREAD_PRIORITY), 0, K_NO_WAIT);
139 #elif defined(CONFIG_ICM42670_TRIGGER_GLOBAL_THREAD)
140 	data->work.handler = icm42670_work_handler;
141 #endif
142 
143 	return gpio_pin_interrupt_configure_dt(&cfg->gpio_int, GPIO_INT_EDGE_TO_ACTIVE);
144 }
145 
icm42670_trigger_enable_interrupt(const struct device * dev)146 int icm42670_trigger_enable_interrupt(const struct device *dev)
147 {
148 	int res;
149 	const struct icm42670_config *cfg = dev->config;
150 
151 	/* pulse-mode (auto clearing), push-pull and active-high */
152 	res = icm42670_spi_single_write(&cfg->spi, REG_INT_CONFIG,
153 					BIT_INT1_DRIVE_CIRCUIT | BIT_INT1_POLARITY);
154 
155 	if (res) {
156 		return res;
157 	}
158 
159 	/* enable data ready interrupt on INT1 pin */
160 	return icm42670_spi_single_write(&cfg->spi, REG_INT_SOURCE0, BIT_INT_DRDY_INT1_EN);
161 }
162 
icm42670_lock(const struct device * dev)163 void icm42670_lock(const struct device *dev)
164 {
165 	struct icm42670_data *data = dev->data;
166 
167 	k_mutex_lock(&data->mutex, K_FOREVER);
168 }
169 
icm42670_unlock(const struct device * dev)170 void icm42670_unlock(const struct device *dev)
171 {
172 	struct icm42670_data *data = dev->data;
173 
174 	k_mutex_unlock(&data->mutex);
175 }
176