1 /* Bosch BMM150 pressure sensor
2  *
3  * Copyright (c) 2020 Facebook, Inc. and its affiliates
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  *
7  * Datasheet:
8  * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmm150-ds001.pdf
9  */
10 
11 #include <zephyr/kernel.h>
12 #include <zephyr/pm/device.h>
13 #include <zephyr/logging/log.h>
14 
15 #include "bmm150.h"
16 
17 LOG_MODULE_DECLARE(BMM150, CONFIG_SENSOR_LOG_LEVEL);
18 
bmm150_handle_interrupts(const void * arg)19 static void bmm150_handle_interrupts(const void *arg)
20 {
21 	const struct device *dev = (const struct device *)arg;
22 	struct bmm150_data *data = dev->data;
23 
24 	if (data->drdy_handler) {
25 		data->drdy_handler(dev, data->drdy_trigger);
26 	}
27 }
28 
29 #ifdef CONFIG_BMM150_TRIGGER_OWN_THREAD
30 static K_THREAD_STACK_DEFINE(bmm150_thread_stack,
31 			     CONFIG_BMM150_THREAD_STACK_SIZE);
32 static struct k_thread bmm150_thread;
33 
bmm150_thread_main(void * arg1,void * unused1,void * unused2)34 static void bmm150_thread_main(void *arg1, void *unused1, void *unused2)
35 {
36 	ARG_UNUSED(unused1);
37 	ARG_UNUSED(unused2);
38 	const struct device *dev = (const struct device *)arg1;
39 	struct bmm150_data *data = dev->data;
40 
41 	while (1) {
42 		k_sem_take(&data->sem, K_FOREVER);
43 		bmm150_handle_interrupts(dev);
44 	}
45 }
46 #endif
47 
48 #ifdef CONFIG_BMM150_TRIGGER_GLOBAL_THREAD
bmm150_work_handler(struct k_work * work)49 static void bmm150_work_handler(struct k_work *work)
50 {
51 	struct bmm150_data *data = CONTAINER_OF(work,
52 						struct bmm150_data,
53 						work);
54 
55 	bmm150_handle_interrupts(data->dev);
56 }
57 #endif
58 
bmm150_gpio_callback(const struct device * port,struct gpio_callback * cb,uint32_t pin)59 static void bmm150_gpio_callback(const struct device *port,
60 				 struct gpio_callback *cb,
61 				 uint32_t pin)
62 {
63 	struct bmm150_data *data = CONTAINER_OF(cb,
64 						struct bmm150_data,
65 						gpio_cb);
66 
67 	ARG_UNUSED(port);
68 	ARG_UNUSED(pin);
69 
70 #if defined(CONFIG_BMM150_TRIGGER_OWN_THREAD)
71 	k_sem_give(&data->sem);
72 #elif defined(CONFIG_BMM150_TRIGGER_GLOBAL_THREAD)
73 	k_work_submit(&data->work);
74 #elif defined(CONFIG_BMM150_TRIGGER_DIRECT)
75 	bmm150_handle_interrupts(data->dev);
76 #endif
77 }
78 
bmm150_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)79 int bmm150_trigger_set(
80 	const struct device *dev,
81 	const struct sensor_trigger *trig,
82 	sensor_trigger_handler_t handler)
83 {
84 	uint16_t values[BMM150_AXIS_XYZR_MAX];
85 	struct bmm150_data *data = dev->data;
86 	const struct bmm150_config *cfg = dev->config;
87 
88 #ifdef CONFIG_PM_DEVICE
89 	enum pm_device_state state;
90 
91 	(void)pm_device_state_get(dev, &state);
92 	if (state != PM_DEVICE_STATE_ACTIVE) {
93 		return -EBUSY;
94 	}
95 #endif
96 
97 	if (trig->type != SENSOR_TRIG_DATA_READY) {
98 		return -ENOTSUP;
99 	}
100 
101 	data->drdy_trigger = trig;
102 	data->drdy_handler = handler;
103 
104 	if (bmm150_reg_update_byte(dev,
105 				   BMM150_REG_INT_DRDY,
106 				   BMM150_MASK_DRDY_EN,
107 				    (handler != NULL) << BMM150_SHIFT_DRDY_EN) < 0) {
108 		LOG_ERR("Failed to enable DRDY interrupt");
109 		return -EIO;
110 	}
111 
112 	/* Clean data registers */
113 	if (cfg->bus_io->read(&cfg->bus, BMM150_REG_X_L, (uint8_t *)values, sizeof(values)) < 0) {
114 		LOG_ERR("failed to read sample");
115 		return -EIO;
116 	}
117 
118 	return 0;
119 }
120 
bmm150_trigger_mode_init(const struct device * dev)121 int bmm150_trigger_mode_init(const struct device *dev)
122 {
123 	struct bmm150_data *data = dev->data;
124 	const struct bmm150_config *cfg = dev->config;
125 
126 	if (!device_is_ready(cfg->drdy_int.port)) {
127 		LOG_ERR("INT device is not ready");
128 		return -ENODEV;
129 	}
130 
131 #if defined(CONFIG_BMM150_TRIGGER_OWN_THREAD)
132 	k_sem_init(&data->sem, 0, 1);
133 	k_thread_create(
134 		&bmm150_thread,
135 		bmm150_thread_stack,
136 		CONFIG_BMM150_THREAD_STACK_SIZE,
137 		bmm150_thread_main,
138 		(void *)dev,
139 		NULL,
140 		NULL,
141 		K_PRIO_COOP(CONFIG_BMM150_THREAD_PRIORITY),
142 		0,
143 		K_NO_WAIT);
144 #elif defined(CONFIG_BMM150_TRIGGER_GLOBAL_THREAD)
145 	k_work_init(&data->work, bmm150_work_handler);
146 #endif
147 
148 #if defined(CONFIG_BMM150_TRIGGER_GLOBAL_THREAD) || \
149 	defined(CONFIG_BMM150_TRIGGER_DIRECT)
150 	data->dev = dev;
151 #endif
152 
153 	gpio_init_callback(&data->gpio_cb,
154 			bmm150_gpio_callback,
155 			BIT(cfg->drdy_int.pin));
156 
157 	return gpio_add_callback(cfg->drdy_int.port, &data->gpio_cb);
158 }
159 
bmm150_trigger_mode_power_ctrl(const struct device * dev,bool enable)160 int bmm150_trigger_mode_power_ctrl(const struct device *dev, bool enable)
161 {
162 	const struct bmm150_config *cfg = dev->config;
163 	int ret;
164 
165 	if (enable) {
166 		ret = gpio_pin_configure_dt(&cfg->drdy_int, GPIO_INPUT);
167 		if (ret < 0) {
168 			return ret;
169 		}
170 		ret = gpio_pin_interrupt_configure_dt(&cfg->drdy_int,
171 						GPIO_INT_EDGE_TO_ACTIVE);
172 	} else {
173 		ret  = gpio_pin_interrupt_configure_dt(&cfg->drdy_int, GPIO_INT_DISABLE);
174 		if (ret < 0) {
175 			return ret;
176 		}
177 		ret = gpio_pin_configure_dt(&cfg->drdy_int, GPIO_DISCONNECTED);
178 	}
179 	return ret;
180 }
181