1 /*
2  * Copyright (c) 2023 Elektronikutvecklingsbyrån EUB AB
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/device.h>
8 #include <zephyr/logging/log.h>
9 LOG_MODULE_DECLARE(bmi270);
10 
11 #include "bmi270.h"
12 
13 enum {
14 	INT_FLAGS_INT1,
15 	INT_FLAGS_INT2,
16 };
17 
bmi270_raise_int_flag(const struct device * dev,int bit)18 static void bmi270_raise_int_flag(const struct device *dev, int bit)
19 {
20 	struct bmi270_data *data = dev->data;
21 
22 	atomic_set_bit(&data->int_flags, bit);
23 
24 #if defined(CONFIG_BMI270_TRIGGER_OWN_THREAD)
25 	k_sem_give(&data->trig_sem);
26 #elif defined(CONFIG_BMI270_TRIGGER_GLOBAL_THREAD)
27 	k_work_submit(&data->trig_work);
28 #endif
29 }
30 
bmi270_int1_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)31 static void bmi270_int1_callback(const struct device *dev,
32 				 struct gpio_callback *cb, uint32_t pins)
33 {
34 	struct bmi270_data *data =
35 		CONTAINER_OF(cb, struct bmi270_data, int1_cb);
36 	bmi270_raise_int_flag(data->dev, INT_FLAGS_INT1);
37 }
38 
bmi270_int2_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)39 static void bmi270_int2_callback(const struct device *dev,
40 				 struct gpio_callback *cb, uint32_t pins)
41 {
42 	struct bmi270_data *data =
43 		CONTAINER_OF(cb, struct bmi270_data, int2_cb);
44 	bmi270_raise_int_flag(data->dev, INT_FLAGS_INT2);
45 }
46 
47 
bmi270_thread_cb(const struct device * dev)48 static void bmi270_thread_cb(const struct device *dev)
49 {
50 	struct bmi270_data *data = dev->data;
51 	int ret;
52 
53 	/* INT1 is used for feature interrupts */
54 	if (atomic_test_and_clear_bit(&data->int_flags, INT_FLAGS_INT1)) {
55 		uint16_t int_status;
56 
57 		ret = bmi270_reg_read(dev, BMI270_REG_INT_STATUS_0,
58 			(uint8_t *)&int_status, sizeof(int_status));
59 		if (ret < 0) {
60 			LOG_ERR("read interrupt status returned %d", ret);
61 			return;
62 		}
63 
64 		k_mutex_lock(&data->trigger_mutex, K_FOREVER);
65 
66 		if (data->motion_handler != NULL) {
67 			if (int_status & BMI270_INT_STATUS_ANY_MOTION) {
68 				data->motion_handler(dev, data->motion_trigger);
69 			}
70 		}
71 
72 		k_mutex_unlock(&data->trigger_mutex);
73 	}
74 
75 	/* INT2 is used for data ready interrupts */
76 	if (atomic_test_and_clear_bit(&data->int_flags, INT_FLAGS_INT2)) {
77 		k_mutex_lock(&data->trigger_mutex, K_FOREVER);
78 
79 		if (data->drdy_handler != NULL) {
80 			data->drdy_handler(dev, data->drdy_trigger);
81 		}
82 
83 		k_mutex_unlock(&data->trigger_mutex);
84 	}
85 }
86 
87 #ifdef CONFIG_BMI270_TRIGGER_OWN_THREAD
bmi270_thread(struct bmi270_data * data)88 static void bmi270_thread(struct bmi270_data *data)
89 {
90 	while (1) {
91 		k_sem_take(&data->trig_sem, K_FOREVER);
92 		bmi270_thread_cb(data->dev);
93 	}
94 }
95 #endif
96 
97 #ifdef CONFIG_BMI270_TRIGGER_GLOBAL_THREAD
bmi270_trig_work_cb(struct k_work * work)98 static void bmi270_trig_work_cb(struct k_work *work)
99 {
100 	struct bmi270_data *data =
101 		CONTAINER_OF(work, struct bmi270_data, trig_work);
102 
103 	bmi270_thread_cb(data->dev);
104 }
105 #endif
106 
bmi270_feature_reg_write(const struct device * dev,const struct bmi270_feature_reg * reg,uint16_t value)107 static int bmi270_feature_reg_write(const struct device *dev,
108 			     const struct bmi270_feature_reg *reg,
109 			     uint16_t value)
110 {
111 	int ret;
112 	uint8_t feat_page = reg->page;
113 
114 	ret = bmi270_reg_write(dev, BMI270_REG_FEAT_PAGE, &feat_page, 1);
115 	if (ret < 0) {
116 		LOG_ERR("bmi270_reg_write (0x%02x) failed: %d", BMI270_REG_FEAT_PAGE, ret);
117 		return ret;
118 	}
119 
120 	LOG_DBG("feature reg[0x%02x]@%d = 0x%04x", reg->addr, reg->page, value);
121 
122 	ret = bmi270_reg_write(dev, reg->addr, (uint8_t *)&value, 2);
123 	if (ret < 0) {
124 		LOG_ERR("bmi270_reg_write (0x%02x) failed: %d", reg->addr, ret);
125 		return ret;
126 	}
127 
128 	return 0;
129 }
130 
bmi270_init_int_pin(const struct gpio_dt_spec * pin,struct gpio_callback * pin_cb,gpio_callback_handler_t handler)131 static int bmi270_init_int_pin(const struct gpio_dt_spec *pin,
132 			       struct gpio_callback *pin_cb,
133 			       gpio_callback_handler_t handler)
134 {
135 	int ret;
136 
137 	if (!pin->port) {
138 		return 0;
139 	}
140 
141 	if (!device_is_ready(pin->port)) {
142 		LOG_DBG("%s not ready", pin->port->name);
143 		return -ENODEV;
144 	}
145 
146 	gpio_init_callback(pin_cb, handler, BIT(pin->pin));
147 
148 	ret = gpio_pin_configure_dt(pin, GPIO_INPUT);
149 	if (ret) {
150 		return ret;
151 	}
152 
153 	ret = gpio_pin_interrupt_configure_dt(pin, GPIO_INT_EDGE_TO_ACTIVE);
154 	if (ret) {
155 		return ret;
156 	}
157 
158 	ret = gpio_add_callback(pin->port, pin_cb);
159 	if (ret) {
160 		return ret;
161 	}
162 
163 	return 0;
164 }
165 
166 
bmi270_init_interrupts(const struct device * dev)167 int bmi270_init_interrupts(const struct device *dev)
168 {
169 	const struct bmi270_config *cfg = dev->config;
170 	struct bmi270_data *data = dev->data;
171 	int ret;
172 
173 #if CONFIG_BMI270_TRIGGER_OWN_THREAD
174 	k_sem_init(&data->trig_sem, 0, 1);
175 	k_thread_create(&data->thread, data->thread_stack, CONFIG_BMI270_THREAD_STACK_SIZE,
176 			(k_thread_entry_t)bmi270_thread, data, NULL, NULL,
177 			K_PRIO_COOP(CONFIG_BMI270_THREAD_PRIORITY), 0, K_NO_WAIT);
178 #elif CONFIG_BMI270_TRIGGER_GLOBAL_THREAD
179 	k_work_init(&data->trig_work, bmi270_trig_work_cb);
180 #endif
181 
182 	ret = bmi270_init_int_pin(&cfg->int1, &data->int1_cb,
183 				  bmi270_int1_callback);
184 	if (ret) {
185 		LOG_ERR("Failed to initialize INT1");
186 		return -EINVAL;
187 	}
188 
189 	ret = bmi270_init_int_pin(&cfg->int2, &data->int2_cb,
190 				  bmi270_int2_callback);
191 	if (ret) {
192 		LOG_ERR("Failed to initialize INT2");
193 		return -EINVAL;
194 	}
195 
196 	if (cfg->int1.port) {
197 		uint8_t int1_io_ctrl = BMI270_INT_IO_CTRL_OUTPUT_EN;
198 
199 		ret = bmi270_reg_write(dev, BMI270_REG_INT1_IO_CTRL, &int1_io_ctrl, 1);
200 		if (ret < 0) {
201 			LOG_ERR("failed configuring INT1_IO_CTRL (%d)", ret);
202 			return ret;
203 		}
204 	}
205 
206 	if (cfg->int2.port) {
207 		uint8_t int2_io_ctrl = BMI270_INT_IO_CTRL_OUTPUT_EN;
208 
209 		ret = bmi270_reg_write(dev, BMI270_REG_INT2_IO_CTRL, &int2_io_ctrl, 1);
210 		if (ret < 0) {
211 			LOG_ERR("failed configuring INT2_IO_CTRL (%d)", ret);
212 			return ret;
213 		}
214 	}
215 
216 	return 0;
217 }
218 
bmi270_anymo_config(const struct device * dev,bool enable)219 static int bmi270_anymo_config(const struct device *dev, bool enable)
220 {
221 	const struct bmi270_config *cfg = dev->config;
222 	struct bmi270_data *data = dev->data;
223 	uint16_t anymo_2;
224 	int ret;
225 
226 	if (enable) {
227 		ret = bmi270_feature_reg_write(dev, cfg->feature->anymo_1,
228 					       data->anymo_1);
229 		if (ret < 0) {
230 			return ret;
231 		}
232 	}
233 
234 	anymo_2 = data->anymo_2;
235 	if (enable) {
236 		anymo_2 |= BMI270_ANYMO_2_ENABLE;
237 	}
238 
239 	ret = bmi270_feature_reg_write(dev, cfg->feature->anymo_2, anymo_2);
240 	if (ret < 0) {
241 		return ret;
242 	}
243 
244 	uint8_t int1_map_feat = 0;
245 
246 	if (enable) {
247 		int1_map_feat |= BMI270_INT_MAP_ANY_MOTION;
248 	}
249 
250 	ret = bmi270_reg_write(dev, BMI270_REG_INT1_MAP_FEAT, &int1_map_feat, 1);
251 	if (ret < 0) {
252 		LOG_ERR("failed configuring INT1_MAP_FEAT (%d)", ret);
253 		return ret;
254 	}
255 
256 	return 0;
257 }
258 
bmi270_drdy_config(const struct device * dev,bool enable)259 static int bmi270_drdy_config(const struct device *dev, bool enable)
260 {
261 	int ret;
262 
263 	uint8_t int_map_data = 0;
264 
265 	if (enable) {
266 		int_map_data |= BMI270_INT_MAP_DATA_DRDY_INT2;
267 	}
268 
269 	ret = bmi270_reg_write(dev, BMI270_REG_INT_MAP_DATA, &int_map_data, 1);
270 	if (ret < 0) {
271 		LOG_ERR("failed configuring INT_MAP_DATA (%d)", ret);
272 		return ret;
273 	}
274 
275 	return 0;
276 }
277 
bmi270_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)278 int bmi270_trigger_set(const struct device *dev,
279 		       const struct sensor_trigger *trig,
280 		       sensor_trigger_handler_t handler)
281 {
282 	struct bmi270_data *data = dev->data;
283 	const struct bmi270_config *cfg = dev->config;
284 
285 	switch (trig->type) {
286 	case SENSOR_TRIG_MOTION:
287 		if (!cfg->int1.port) {
288 			return -ENOTSUP;
289 		}
290 
291 		k_mutex_lock(&data->trigger_mutex, K_FOREVER);
292 		data->motion_handler = handler;
293 		data->motion_trigger = trig;
294 		k_mutex_unlock(&data->trigger_mutex);
295 		return bmi270_anymo_config(dev, handler != NULL);
296 
297 	case SENSOR_TRIG_DATA_READY:
298 		if (!cfg->int2.port) {
299 			return -ENOTSUP;
300 		}
301 
302 		k_mutex_lock(&data->trigger_mutex, K_FOREVER);
303 		data->drdy_handler = handler;
304 		data->drdy_trigger = trig;
305 		k_mutex_unlock(&data->trigger_mutex);
306 		return bmi270_drdy_config(dev, handler != NULL);
307 	default:
308 		return -ENOTSUP;
309 	}
310 
311 	return 0;
312 }
313