1 /* Bosch BMI08X inertial measurement unit driver, trigger implementation
2  *
3  * Copyright (c) 2022 Meta Platforms, Inc. and its affiliates
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <zephyr/drivers/gpio.h>
9 #include <zephyr/drivers/sensor.h>
10 #include <zephyr/pm/device.h>
11 #include <zephyr/kernel.h>
12 
13 #define DT_DRV_COMPAT bosch_bmi08x_gyro
14 #include "bmi08x.h"
15 
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_DECLARE(BMI08X_GYRO, CONFIG_SENSOR_LOG_LEVEL);
18 
bmi08x_handle_drdy_gyr(const struct device * dev)19 static void bmi08x_handle_drdy_gyr(const struct device *dev)
20 {
21 	struct bmi08x_gyro_data *data = dev->data;
22 
23 	if (data->handler_drdy_gyr) {
24 		data->handler_drdy_gyr(dev, data->drdy_trig_gyr);
25 	}
26 }
27 
bmi08x_handle_interrupts_gyr(void * arg)28 static void bmi08x_handle_interrupts_gyr(void *arg)
29 {
30 	const struct device *dev = (const struct device *)arg;
31 
32 	bmi08x_handle_drdy_gyr(dev);
33 }
34 
35 #ifdef CONFIG_BMI08X_GYRO_TRIGGER_OWN_THREAD
bmi08x_gyr_thread_main(void * arg1,void * unused1,void * unused2)36 static void bmi08x_gyr_thread_main(void *arg1, void *unused1, void *unused2)
37 {
38 	k_thread_name_set(NULL, "bmi08x_gyr_trig");
39 
40 	ARG_UNUSED(unused1);
41 	ARG_UNUSED(unused2);
42 
43 	const struct device *dev = (const struct device *)arg1;
44 	struct bmi08x_gyro_data *data = dev->data;
45 
46 	while (1) {
47 		k_sem_take(&data->sem, K_FOREVER);
48 		bmi08x_handle_interrupts_gyr((void *)dev);
49 	}
50 }
51 #endif
52 
53 #ifdef CONFIG_BMI08X_GYRO_TRIGGER_GLOBAL_THREAD
bmi08x_gyr_work_handler(struct k_work * work)54 static void bmi08x_gyr_work_handler(struct k_work *work)
55 {
56 	struct bmi08x_gyro_data *data = CONTAINER_OF(work, struct bmi08x_gyro_data, work);
57 
58 	bmi08x_handle_interrupts_gyr((void *)data->dev);
59 }
60 #endif
61 
bmi08x_gyr_gpio_callback(const struct device * port,struct gpio_callback * cb,uint32_t pin)62 static void bmi08x_gyr_gpio_callback(const struct device *port, struct gpio_callback *cb,
63 				     uint32_t pin)
64 {
65 	struct bmi08x_gyro_data *data = CONTAINER_OF(cb, struct bmi08x_gyro_data, gpio_cb);
66 
67 	ARG_UNUSED(port);
68 	ARG_UNUSED(pin);
69 
70 #if defined(CONFIG_BMI08X_GYRO_TRIGGER_OWN_THREAD)
71 	k_sem_give(&data->sem);
72 #elif defined(CONFIG_BMI08X_GYRO_TRIGGER_GLOBAL_THREAD)
73 	k_work_submit(&data->work);
74 #endif
75 }
76 
bmi08x_trigger_set_gyr(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)77 int bmi08x_trigger_set_gyr(const struct device *dev, const struct sensor_trigger *trig,
78 			   sensor_trigger_handler_t handler)
79 {
80 	struct bmi08x_gyro_data *data = dev->data;
81 
82 	if ((trig->chan == SENSOR_CHAN_GYRO_XYZ) && (trig->type == SENSOR_TRIG_DATA_READY)) {
83 		data->drdy_trig_gyr = trig;
84 		data->handler_drdy_gyr = handler;
85 		return 0;
86 	}
87 
88 	return -ENOTSUP;
89 }
90 
bmi08x_gyr_trigger_mode_init(const struct device * dev)91 int bmi08x_gyr_trigger_mode_init(const struct device *dev)
92 {
93 	struct bmi08x_gyro_data *data = dev->data;
94 	const struct bmi08x_gyro_config *cfg = dev->config;
95 	int ret;
96 
97 	if (!gpio_is_ready_dt(&cfg->int_gpio)) {
98 		LOG_ERR("GPIO device not ready");
99 		return -ENODEV;
100 	}
101 
102 #if defined(CONFIG_BMI08X_GYRO_TRIGGER_OWN_THREAD)
103 	k_sem_init(&data->sem, 0, K_SEM_MAX_LIMIT);
104 
105 	k_thread_create(&data->thread, data->thread_stack,
106 			CONFIG_BMI08X_GYRO_THREAD_STACK_SIZE, bmi08x_gyr_thread_main, (void *)dev,
107 			NULL, NULL, K_PRIO_COOP(CONFIG_BMI08X_GYRO_THREAD_PRIORITY), 0, K_NO_WAIT);
108 #elif defined(CONFIG_BMI08X_GYRO_TRIGGER_GLOBAL_THREAD)
109 	data->work.handler = bmi08x_gyr_work_handler;
110 	data->dev = dev;
111 #endif
112 
113 	gpio_pin_configure_dt(&cfg->int_gpio, GPIO_INPUT);
114 
115 	gpio_init_callback(&data->gpio_cb, bmi08x_gyr_gpio_callback, BIT(cfg->int_gpio.pin));
116 
117 	ret = gpio_add_callback(cfg->int_gpio.port, &data->gpio_cb);
118 
119 	if (ret < 0) {
120 		LOG_ERR("Failed to set gpio callback.");
121 		return ret;
122 	}
123 	gpio_pin_interrupt_configure_dt(&cfg->int_gpio, GPIO_INT_EDGE_TO_ACTIVE);
124 
125 	return ret;
126 }
127