1 /* Bosch BMG160 gyro driver, trigger implementation
2  *
3  * Copyright (c) 2016 Intel Corporation
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  *
7  * Datasheet:
8  * http://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BMG160-DS000-09.pdf
9  */
10 
11 #include <zephyr/kernel.h>
12 #include <zephyr/drivers/sensor.h>
13 
14 #include "bmg160.h"
15 
16 extern struct bmg160_device_data bmg160_data;
17 
18 #include <zephyr/logging/log.h>
19 LOG_MODULE_DECLARE(BMG160, CONFIG_SENSOR_LOG_LEVEL);
20 
setup_int(const struct device * dev,bool enable)21 static inline int setup_int(const struct device *dev,
22 			      bool enable)
23 {
24 	const struct bmg160_device_config *cfg = dev->config;
25 
26 	return gpio_pin_interrupt_configure_dt(&cfg->int_gpio,
27 					       enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE);
28 }
29 
bmg160_gpio_callback(const struct device * port,struct gpio_callback * cb,uint32_t pin)30 static void bmg160_gpio_callback(const struct device *port,
31 				 struct gpio_callback *cb,
32 				 uint32_t pin)
33 {
34 	struct bmg160_device_data *bmg160 =
35 		CONTAINER_OF(cb, struct bmg160_device_data, gpio_cb);
36 
37 	ARG_UNUSED(port);
38 	ARG_UNUSED(pin);
39 
40 #if defined(CONFIG_BMG160_TRIGGER_OWN_THREAD)
41 	k_sem_give(&bmg160->trig_sem);
42 #elif defined(CONFIG_BMG160_TRIGGER_GLOBAL_THREAD)
43 	k_work_submit(&bmg160->work);
44 #endif
45 }
46 
bmg160_anymotion_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)47 static int bmg160_anymotion_set(const struct device *dev,
48 				const struct sensor_trigger *trig,
49 				sensor_trigger_handler_t handler)
50 {
51 	struct bmg160_device_data *bmg160 = dev->data;
52 	uint8_t anymotion_en = 0U;
53 
54 	if (handler) {
55 		anymotion_en = BMG160_ANY_EN_X |
56 			       BMG160_ANY_EN_Y |
57 			       BMG160_ANY_EN_Z;
58 	}
59 
60 	if (bmg160_update_byte(dev, BMG160_REG_ANY_EN,
61 			       BMG160_ANY_EN_MASK, anymotion_en) < 0) {
62 		return -EIO;
63 	}
64 
65 	bmg160->anymotion_handler = handler;
66 	bmg160->anymotion_trig = trig;
67 
68 	return 0;
69 }
70 
bmg160_drdy_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)71 static int bmg160_drdy_set(const struct device *dev,
72 			   const struct sensor_trigger *trig,
73 			   sensor_trigger_handler_t handler)
74 {
75 	struct bmg160_device_data *bmg160 = dev->data;
76 
77 	if (bmg160_update_byte(dev, BMG160_REG_INT_EN0,
78 			       BMG160_DATA_EN,
79 			       handler ? BMG160_DATA_EN : 0) < 0) {
80 		return -EIO;
81 	}
82 
83 	bmg160->drdy_handler = handler;
84 	bmg160->drdy_trig = trig;
85 
86 	return 0;
87 }
88 
bmg160_slope_config(const struct device * dev,enum sensor_attribute attr,const struct sensor_value * val)89 int bmg160_slope_config(const struct device *dev, enum sensor_attribute attr,
90 			const struct sensor_value *val)
91 {
92 	struct bmg160_device_data *bmg160 = dev->data;
93 
94 	if (attr == SENSOR_ATTR_SLOPE_TH) {
95 		uint16_t any_th_dps, range_dps;
96 		uint8_t any_th_reg_val;
97 
98 		any_th_dps = sensor_rad_to_degrees(val);
99 		range_dps = BMG160_SCALE_TO_RANGE(bmg160->scale);
100 		any_th_reg_val = any_th_dps * 2000U / range_dps;
101 
102 		/* the maximum slope depends on selected range */
103 		if (any_th_dps > range_dps / 16U) {
104 			return -ENOTSUP;
105 		}
106 
107 		return bmg160_write_byte(dev, BMG160_REG_THRES,
108 					 any_th_dps & BMG160_THRES_MASK);
109 	} else if (attr == SENSOR_ATTR_SLOPE_DUR) {
110 		/* slope duration can be 4, 8, 12 or 16 samples */
111 		if (val->val1 != 4 && val->val1 != 8 &&
112 		    val->val1 != 12 && val->val1 != 16) {
113 			return -ENOTSUP;
114 		}
115 
116 		return bmg160_write_byte(dev, BMG160_REG_ANY_EN,
117 			   (val->val1 << BMG160_ANY_DURSAMPLE_POS) &
118 			    BMG160_ANY_DURSAMPLE_MASK);
119 	}
120 
121 	return -ENOTSUP;
122 }
123 
bmg160_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)124 int bmg160_trigger_set(const struct device *dev,
125 		       const struct sensor_trigger *trig,
126 		       sensor_trigger_handler_t handler)
127 {
128 	const struct bmg160_device_config *config = dev->config;
129 
130 	if (!config->int_gpio.port) {
131 		return -ENOTSUP;
132 	}
133 
134 	if (trig->type == SENSOR_TRIG_DELTA) {
135 		return bmg160_anymotion_set(dev, trig, handler);
136 	} else if (trig->type == SENSOR_TRIG_DATA_READY) {
137 		return bmg160_drdy_set(dev, trig, handler);
138 	}
139 
140 	return -ENOTSUP;
141 }
142 
bmg160_handle_anymotion_int(const struct device * dev)143 static int bmg160_handle_anymotion_int(const struct device *dev)
144 {
145 	struct bmg160_device_data *bmg160 = dev->data;
146 
147 	if (bmg160->anymotion_handler) {
148 		bmg160->anymotion_handler(dev, bmg160->anymotion_trig);
149 	}
150 
151 	return 0;
152 }
153 
bmg160_handle_dataready_int(const struct device * dev)154 static int bmg160_handle_dataready_int(const struct device *dev)
155 {
156 	struct bmg160_device_data *bmg160 = dev->data;
157 
158 	if (bmg160->drdy_handler) {
159 		bmg160->drdy_handler(dev, bmg160->drdy_trig);
160 	}
161 
162 	return 0;
163 }
164 
bmg160_handle_int(const struct device * dev)165 static void bmg160_handle_int(const struct device *dev)
166 {
167 	uint8_t status_int[4];
168 
169 	if (bmg160_read(dev, BMG160_REG_INT_STATUS0, status_int, 4) < 0) {
170 		return;
171 	}
172 
173 	if (status_int[0] & BMG160_ANY_INT) {
174 		bmg160_handle_anymotion_int(dev);
175 	} else {
176 		bmg160_handle_dataready_int(dev);
177 	}
178 }
179 
180 #ifdef CONFIG_BMG160_TRIGGER_OWN_THREAD
181 static K_KERNEL_STACK_DEFINE(bmg160_thread_stack, CONFIG_BMG160_THREAD_STACK_SIZE);
182 static struct k_thread bmg160_thread;
183 
bmg160_thread_main(void * p1,void * p2,void * p3)184 static void bmg160_thread_main(void *p1, void *p2, void *p3)
185 {
186 	ARG_UNUSED(p2);
187 	ARG_UNUSED(p3);
188 
189 	struct bmg160_device_data *bmg160 = p1;
190 
191 	while (true) {
192 		k_sem_take(&bmg160->trig_sem, K_FOREVER);
193 
194 		bmg160_handle_int(bmg160->dev);
195 	}
196 }
197 #endif
198 
199 #ifdef CONFIG_BMG160_TRIGGER_GLOBAL_THREAD
bmg160_work_cb(struct k_work * work)200 static void bmg160_work_cb(struct k_work *work)
201 {
202 	struct bmg160_device_data *bmg160 =
203 		CONTAINER_OF(work, struct bmg160_device_data, work);
204 
205 	bmg160_handle_int(bmg160->dev);
206 }
207 #endif
208 
bmg160_trigger_init(const struct device * dev)209 int bmg160_trigger_init(const struct device *dev)
210 {
211 	const struct bmg160_device_config *cfg = dev->config;
212 	struct bmg160_device_data *bmg160 = dev->data;
213 	int ret;
214 
215 	/* set INT1 pin to: push-pull, active low */
216 	if (bmg160_write_byte(dev, BMG160_REG_INT_EN1, 0) < 0) {
217 		LOG_DBG("Failed to select interrupt pins type.");
218 		return -EIO;
219 	}
220 
221 	/* set interrupt mode to non-latched */
222 	if (bmg160_write_byte(dev, BMG160_REG_INT_RST_LATCH, 0) < 0) {
223 		LOG_DBG("Failed to set the interrupt mode.");
224 		return -EIO;
225 	}
226 
227 	/* map anymotion and high rate interrupts to INT1 pin */
228 	if (bmg160_write_byte(dev, BMG160_REG_INT_MAP0,
229 			      BMG160_INT1_ANY | BMG160_INT1_HIGH) < 0) {
230 		LOG_DBG("Unable to map interrupts.");
231 		return -EIO;
232 	}
233 
234 	/* map data ready, FIFO and FastOffset interrupts to INT1 pin */
235 	if (bmg160_write_byte(dev, BMG160_REG_INT_MAP1,
236 			      BMG160_INT1_DATA | BMG160_INT1_FIFO |
237 			      BMG160_INT1_FAST_OFFSET) < 0) {
238 		LOG_DBG("Unable to map interrupts.");
239 		return -EIO;
240 	}
241 
242 	if (!gpio_is_ready_dt(&cfg->int_gpio)) {
243 		LOG_ERR("GPIO device not ready");
244 		return -ENODEV;
245 	}
246 
247 	bmg160->dev = dev;
248 
249 #if defined(CONFIG_BMG160_TRIGGER_OWN_THREAD)
250 	k_sem_init(&bmg160->trig_sem, 0, K_SEM_MAX_LIMIT);
251 	k_thread_create(&bmg160_thread, bmg160_thread_stack,
252 			CONFIG_BMG160_THREAD_STACK_SIZE,
253 			bmg160_thread_main,
254 			bmg160, NULL, NULL,
255 			K_PRIO_COOP(CONFIG_BMG160_THREAD_PRIORITY), 0,
256 			K_NO_WAIT);
257 
258 #elif defined(CONFIG_BMG160_TRIGGER_GLOBAL_THREAD)
259 	bmg160->work.handler = bmg160_work_cb;
260 #endif
261 
262 	ret = gpio_pin_configure_dt(&cfg->int_gpio, GPIO_INT_EDGE_TO_ACTIVE);
263 	if (ret < 0) {
264 		return ret;
265 	}
266 
267 	gpio_init_callback(&bmg160->gpio_cb, bmg160_gpio_callback,
268 			   BIT(cfg->int_gpio.pin));
269 
270 	ret = gpio_add_callback(cfg->int_gpio.port, &bmg160->gpio_cb);
271 	if (ret < 0) {
272 		return ret;
273 	}
274 
275 	return setup_int(dev, true);
276 }
277