1 /*
2  * Copyright (c) 2017, NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nxp_fxas21002
8 
9 #include <zephyr/logging/log.h>
10 
11 #include "fxas21002.h"
12 
13 LOG_MODULE_DECLARE(FXAS21002, CONFIG_SENSOR_LOG_LEVEL);
14 
fxas21002_gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pin_mask)15 static void fxas21002_gpio_callback(const struct device *dev,
16 				    struct gpio_callback *cb,
17 				    uint32_t pin_mask)
18 {
19 	struct fxas21002_data *data =
20 		CONTAINER_OF(cb, struct fxas21002_data, gpio_cb);
21 	const struct fxas21002_config *config = data->dev->config;
22 
23 	if ((pin_mask & BIT(config->int_gpio.pin)) == 0U) {
24 		return;
25 	}
26 
27 	gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_DISABLE);
28 
29 #if defined(CONFIG_FXAS21002_TRIGGER_OWN_THREAD)
30 	k_sem_give(&data->trig_sem);
31 #elif defined(CONFIG_FXAS21002_TRIGGER_GLOBAL_THREAD)
32 	k_work_submit(&data->work);
33 #endif
34 }
35 
fxas21002_handle_drdy_int(const struct device * dev)36 static int fxas21002_handle_drdy_int(const struct device *dev)
37 {
38 	struct fxas21002_data *data = dev->data;
39 
40 	if (data->drdy_handler) {
41 		data->drdy_handler(dev, data->drdy_trig);
42 	}
43 
44 	return 0;
45 }
46 
fxas21002_handle_int(const struct device * dev)47 static void fxas21002_handle_int(const struct device *dev)
48 {
49 	const struct fxas21002_config *config = dev->config;
50 	struct fxas21002_data *data = dev->data;
51 	uint8_t int_source;
52 
53 	k_sem_take(&data->sem, K_FOREVER);
54 
55 	if (config->ops->byte_read(dev, FXAS21002_REG_INT_SOURCE,
56 				   &int_source)) {
57 		LOG_ERR("Could not read interrupt source");
58 		int_source = 0U;
59 	}
60 
61 	k_sem_give(&data->sem);
62 
63 	if (int_source & FXAS21002_INT_SOURCE_DRDY_MASK) {
64 		fxas21002_handle_drdy_int(dev);
65 	}
66 
67 	gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_EDGE_TO_ACTIVE);
68 }
69 
70 #ifdef CONFIG_FXAS21002_TRIGGER_OWN_THREAD
fxas21002_thread_main(struct fxas21002_data * data)71 static void fxas21002_thread_main(struct fxas21002_data *data)
72 {
73 	while (true) {
74 		k_sem_take(&data->trig_sem, K_FOREVER);
75 		fxas21002_handle_int(data->dev);
76 	}
77 }
78 #endif
79 
80 #ifdef CONFIG_FXAS21002_TRIGGER_GLOBAL_THREAD
fxas21002_work_handler(struct k_work * work)81 static void fxas21002_work_handler(struct k_work *work)
82 {
83 	struct fxas21002_data *data =
84 		CONTAINER_OF(work, struct fxas21002_data, work);
85 
86 	fxas21002_handle_int(data->dev);
87 }
88 #endif
89 
fxas21002_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)90 int fxas21002_trigger_set(const struct device *dev,
91 			  const struct sensor_trigger *trig,
92 			  sensor_trigger_handler_t handler)
93 {
94 	const struct fxas21002_config *config = dev->config;
95 	struct fxas21002_data *data = dev->data;
96 	enum fxas21002_power power = FXAS21002_POWER_STANDBY;
97 	uint32_t transition_time;
98 	uint8_t mask;
99 	int ret = 0;
100 
101 	if (!config->int_gpio.port) {
102 		return -ENOTSUP;
103 	}
104 
105 	k_sem_take(&data->sem, K_FOREVER);
106 
107 	switch (trig->type) {
108 	case SENSOR_TRIG_DATA_READY:
109 		mask = FXAS21002_CTRLREG2_CFG_EN_MASK;
110 		data->drdy_handler = handler;
111 		data->drdy_trig = trig;
112 		break;
113 	default:
114 		LOG_ERR("Unsupported sensor trigger");
115 		ret = -ENOTSUP;
116 		goto exit;
117 	}
118 
119 	/* The sensor must be in standby or ready mode when writing the
120 	 * configuration registers, therefore get the current power mode so we
121 	 * can restore it later.
122 	 */
123 	if (fxas21002_get_power(dev, &power)) {
124 		LOG_ERR("Could not get power mode");
125 		ret = -EIO;
126 		goto exit;
127 	}
128 
129 	/* Put the sensor in ready mode */
130 	if (fxas21002_set_power(dev, FXAS21002_POWER_READY)) {
131 		LOG_ERR("Could not set ready mode");
132 		ret = -EIO;
133 		goto exit;
134 	}
135 
136 	/* Configure the sensor interrupt */
137 	if (config->ops->reg_field_update(dev, FXAS21002_REG_CTRLREG2, mask,
138 					  handler ? mask : 0)) {
139 		LOG_ERR("Could not configure interrupt");
140 		ret = -EIO;
141 		goto exit;
142 	}
143 
144 	/* Restore the previous power mode */
145 	if (fxas21002_set_power(dev, power)) {
146 		LOG_ERR("Could not restore power mode");
147 		ret = -EIO;
148 		goto exit;
149 	}
150 
151 	/* Wait the transition time from ready mode */
152 	transition_time = fxas21002_get_transition_time(FXAS21002_POWER_READY,
153 							power,
154 							config->dr);
155 	k_busy_wait(transition_time);
156 
157 exit:
158 	k_sem_give(&data->sem);
159 
160 	return ret;
161 }
162 
fxas21002_trigger_init(const struct device * dev)163 int fxas21002_trigger_init(const struct device *dev)
164 {
165 	const struct fxas21002_config *config = dev->config;
166 	struct fxas21002_data *data = dev->data;
167 	uint8_t ctrl_reg2;
168 	int ret;
169 
170 	data->dev = dev;
171 
172 #if defined(CONFIG_FXAS21002_TRIGGER_OWN_THREAD)
173 	k_sem_init(&data->trig_sem, 0, K_SEM_MAX_LIMIT);
174 	k_thread_create(&data->thread, data->thread_stack,
175 			CONFIG_FXAS21002_THREAD_STACK_SIZE,
176 			(k_thread_entry_t)fxas21002_thread_main, data, 0, NULL,
177 			K_PRIO_COOP(CONFIG_FXAS21002_THREAD_PRIORITY),
178 			0, K_NO_WAIT);
179 #elif defined(CONFIG_FXAS21002_TRIGGER_GLOBAL_THREAD)
180 	data->work.handler = fxas21002_work_handler;
181 #endif
182 
183 	/* Route the interrupts to INT1/INT2 pins */
184 	ctrl_reg2 = 0U;
185 #if CONFIG_FXAS21002_DRDY_INT1
186 	ctrl_reg2 |= FXAS21002_CTRLREG2_CFG_DRDY_MASK;
187 #endif
188 
189 	if (config->ops->byte_write(dev, FXAS21002_REG_CTRLREG2,
190 				    ctrl_reg2)) {
191 		LOG_ERR("Could not configure interrupt pin routing");
192 		return -EIO;
193 	}
194 
195 	if (!gpio_is_ready_dt(&config->int_gpio)) {
196 		LOG_ERR("GPIO device not ready");
197 		return -ENODEV;
198 	}
199 
200 	ret = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT);
201 	if (ret < 0) {
202 		return ret;
203 	}
204 
205 	gpio_init_callback(&data->gpio_cb, fxas21002_gpio_callback,
206 			   BIT(config->int_gpio.pin));
207 
208 	ret = gpio_add_callback(config->int_gpio.port, &data->gpio_cb);
209 	if (ret < 0) {
210 		return ret;
211 	}
212 
213 	ret = gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_EDGE_TO_ACTIVE);
214 	if (ret < 0) {
215 		return ret;
216 	}
217 
218 	return 0;
219 }
220