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