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 #ifdef CONFIG_PM_DEVICE
24 	enum pm_device_state state;
25 
26 	(void)pm_device_state_get(dev, &state);
27 	if (state != PM_DEVICE_STATE_ACTIVE) {
28 		return;
29 	}
30 #endif
31 
32 	if (data->handler_drdy_gyr) {
33 		data->handler_drdy_gyr(dev, data->drdy_trig_gyr);
34 	}
35 }
36 
bmi08x_handle_interrupts_gyr(void * arg)37 static void bmi08x_handle_interrupts_gyr(void *arg)
38 {
39 	const struct device *dev = (const struct device *)arg;
40 
41 	bmi08x_handle_drdy_gyr(dev);
42 }
43 
44 #ifdef CONFIG_BMI08X_GYRO_TRIGGER_OWN_THREAD
bmi08x_gyr_thread_main(void * arg1,void * unused1,void * unused2)45 static void bmi08x_gyr_thread_main(void *arg1, void *unused1, void *unused2)
46 {
47 	k_thread_name_set(NULL, "bmi08x_gyr_trig");
48 
49 	ARG_UNUSED(unused1);
50 	ARG_UNUSED(unused2);
51 
52 	const struct device *dev = (const struct device *)arg1;
53 	struct bmi08x_gyro_data *data = dev->data;
54 
55 	while (1) {
56 		k_sem_take(&data->sem, K_FOREVER);
57 		bmi08x_handle_interrupts_gyr((void *)dev);
58 	}
59 }
60 #endif
61 
62 #ifdef CONFIG_BMI08X_GYRO_TRIGGER_GLOBAL_THREAD
bmi08x_gyr_work_handler(struct k_work * work)63 static void bmi08x_gyr_work_handler(struct k_work *work)
64 {
65 	struct bmi08x_gyro_data *data = CONTAINER_OF(work, struct bmi08x_gyro_data, work);
66 
67 	bmi08x_handle_interrupts_gyr((void *)data->dev);
68 }
69 #endif
70 
bmi08x_gyr_gpio_callback(const struct device * port,struct gpio_callback * cb,uint32_t pin)71 static void bmi08x_gyr_gpio_callback(const struct device *port, struct gpio_callback *cb,
72 				     uint32_t pin)
73 {
74 	struct bmi08x_gyro_data *data = CONTAINER_OF(cb, struct bmi08x_gyro_data, gpio_cb);
75 
76 	ARG_UNUSED(port);
77 	ARG_UNUSED(pin);
78 
79 #if defined(CONFIG_BMI08X_GYRO_TRIGGER_OWN_THREAD)
80 	k_sem_give(&data->sem);
81 #elif defined(CONFIG_BMI08X_GYRO_TRIGGER_GLOBAL_THREAD)
82 	k_work_submit(&data->work);
83 #endif
84 }
85 
bmi08x_trigger_set_gyr(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)86 int bmi08x_trigger_set_gyr(const struct device *dev, const struct sensor_trigger *trig,
87 			   sensor_trigger_handler_t handler)
88 {
89 	struct bmi08x_gyro_data *data = dev->data;
90 
91 	if ((trig->chan == SENSOR_CHAN_GYRO_XYZ) && (trig->type == SENSOR_TRIG_DATA_READY)) {
92 		data->handler_drdy_gyr = handler;
93 		data->drdy_trig_gyr = trig;
94 		return 0;
95 	}
96 
97 	return -ENOTSUP;
98 }
99 
bmi08x_gyr_trigger_mode_init(const struct device * dev)100 int bmi08x_gyr_trigger_mode_init(const struct device *dev)
101 {
102 	struct bmi08x_gyro_data *data = dev->data;
103 	const struct bmi08x_gyro_config *cfg = dev->config;
104 	int ret;
105 
106 	if (!gpio_is_ready_dt(&cfg->int_gpio)) {
107 		LOG_ERR("GPIO device not ready");
108 		return -ENODEV;
109 	}
110 
111 #if defined(CONFIG_BMI08X_GYRO_TRIGGER_OWN_THREAD)
112 	k_sem_init(&data->sem, 0, K_SEM_MAX_LIMIT);
113 
114 	k_thread_create(&data->thread, data->thread_stack,
115 			CONFIG_BMI08X_GYRO_THREAD_STACK_SIZE, bmi08x_gyr_thread_main, (void *)dev,
116 			NULL, NULL, K_PRIO_COOP(CONFIG_BMI08X_GYRO_THREAD_PRIORITY), 0, K_NO_WAIT);
117 #elif defined(CONFIG_BMI08X_GYRO_TRIGGER_GLOBAL_THREAD)
118 	data->work.handler = bmi08x_gyr_work_handler;
119 	data->dev = dev;
120 #endif
121 
122 	gpio_pin_configure_dt(&cfg->int_gpio, GPIO_INPUT);
123 
124 	gpio_init_callback(&data->gpio_cb, bmi08x_gyr_gpio_callback, BIT(cfg->int_gpio.pin));
125 
126 	ret = gpio_add_callback(cfg->int_gpio.port, &data->gpio_cb);
127 
128 	if (ret < 0) {
129 		LOG_ERR("Failed to set gpio callback.");
130 		return ret;
131 	}
132 	gpio_pin_interrupt_configure_dt(&cfg->int_gpio, GPIO_INT_EDGE_TO_ACTIVE);
133 
134 	return ret;
135 }
136