1 /*
2  * Copyright (c) 2018 Analog Devices Inc.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT adi_adxl372
8 
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/gpio.h>
11 #include <zephyr/sys/util.h>
12 #include <zephyr/kernel.h>
13 #include <zephyr/drivers/sensor.h>
14 #include "adxl372.h"
15 
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_DECLARE(ADXL372, CONFIG_SENSOR_LOG_LEVEL);
18 
19 #if defined(CONFIG_ADXL372_TRIGGER_OWN_THREAD) || defined(CONFIG_ADXL372_TRIGGER_GLOBAL_THREAD)
adxl372_thread_cb(const struct device * dev)20 static void adxl372_thread_cb(const struct device *dev)
21 {
22 	const struct adxl372_dev_config *cfg = dev->config;
23 	struct adxl372_data *drv_data = dev->data;
24 	uint8_t status1, status2;
25 	int ret;
26 
27 	/* Clear the status */
28 	if (adxl372_get_status(dev, &status1, &status2, NULL) < 0) {
29 		return;
30 	}
31 
32 	if (drv_data->th_handler != NULL) {
33 		/* In max peak mode we wait until we settle below the inactivity
34 		 * threshold and then call the trigger handler.
35 		 */
36 		if (cfg->max_peak_detect_mode &&
37 			ADXL372_STATUS_2_INACT(status2)) {
38 			drv_data->th_handler(dev, drv_data->th_trigger);
39 		} else if (!cfg->max_peak_detect_mode &&
40 			(ADXL372_STATUS_2_INACT(status2) ||
41 			ADXL372_STATUS_2_ACTIVITY(status2))) {
42 			drv_data->th_handler(dev, drv_data->th_trigger);
43 		}
44 	}
45 
46 	if ((drv_data->drdy_handler != NULL) &&
47 		ADXL372_STATUS_1_DATA_RDY(status1)) {
48 		drv_data->drdy_handler(dev, drv_data->drdy_trigger);
49 	}
50 
51 	ret = gpio_pin_interrupt_configure_dt(&cfg->interrupt,
52 					      GPIO_INT_EDGE_TO_ACTIVE);
53 
54 	__ASSERT(ret == 0, "Interrupt configuration failed");
55 }
56 #endif /* CONFIG_ADXL372_TRIGGER_OWN_THREAD || CONFIG_ADXL372_TRIGGER_GLOBAL_THREAD */
57 
adxl372_gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)58 static void adxl372_gpio_callback(const struct device *dev,
59 				  struct gpio_callback *cb, uint32_t pins)
60 {
61 	struct adxl372_data *drv_data =
62 		CONTAINER_OF(cb, struct adxl372_data, gpio_cb);
63 	const struct adxl372_dev_config *cfg = drv_data->dev->config;
64 
65 	gpio_pin_interrupt_configure_dt(&cfg->interrupt, GPIO_INT_DISABLE);
66 
67 	if (IS_ENABLED(CONFIG_ADXL372_STREAM)) {
68 		adxl372_stream_irq_handler(drv_data->dev);
69 	}
70 
71 #if defined(CONFIG_ADXL372_TRIGGER_OWN_THREAD)
72 	k_sem_give(&drv_data->gpio_sem);
73 #elif defined(CONFIG_ADXL372_TRIGGER_GLOBAL_THREAD)
74 	k_work_submit(&drv_data->work);
75 #endif
76 }
77 
78 #if defined(CONFIG_ADXL372_TRIGGER_OWN_THREAD)
adxl372_thread(void * p1,void * p2,void * p3)79 static void adxl372_thread(void *p1, void *p2, void *p3)
80 {
81 	ARG_UNUSED(p2);
82 	ARG_UNUSED(p3);
83 
84 	struct adxl372_data *drv_data = p1;
85 
86 	while (true) {
87 		k_sem_take(&drv_data->gpio_sem, K_FOREVER);
88 		adxl372_thread_cb(drv_data->dev);
89 	}
90 }
91 
92 #elif defined(CONFIG_ADXL372_TRIGGER_GLOBAL_THREAD)
adxl372_work_cb(struct k_work * work)93 static void adxl372_work_cb(struct k_work *work)
94 {
95 	struct adxl372_data *drv_data =
96 		CONTAINER_OF(work, struct adxl372_data, work);
97 
98 	adxl372_thread_cb(drv_data->dev);
99 }
100 #endif
101 
adxl372_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)102 int adxl372_trigger_set(const struct device *dev,
103 			const struct sensor_trigger *trig,
104 			sensor_trigger_handler_t handler)
105 {
106 	const struct adxl372_dev_config *cfg = dev->config;
107 	struct adxl372_data *drv_data = dev->data;
108 	uint8_t int_mask, int_en, status1, status2;
109 	int ret;
110 
111 	ret = gpio_pin_interrupt_configure_dt(&cfg->interrupt,
112 					      GPIO_INT_DISABLE);
113 	if (ret < 0) {
114 		return ret;
115 	}
116 
117 	switch (trig->type) {
118 	case SENSOR_TRIG_THRESHOLD:
119 		drv_data->th_handler = handler;
120 		drv_data->th_trigger = trig;
121 		int_mask = ADXL372_INT1_MAP_ACT_MSK |
122 			   ADXL372_INT1_MAP_INACT_MSK;
123 		break;
124 	case SENSOR_TRIG_DATA_READY:
125 		drv_data->drdy_handler = handler;
126 		drv_data->drdy_trigger = trig;
127 		int_mask = ADXL372_INT1_MAP_DATA_RDY_MSK;
128 		break;
129 	default:
130 		LOG_ERR("Unsupported sensor trigger");
131 		return -ENOTSUP;
132 	}
133 
134 	if (handler) {
135 		int_en = int_mask;
136 	} else {
137 		int_en = 0U;
138 	}
139 
140 	ret = drv_data->hw_tf->write_reg_mask(dev, ADXL372_INT1_MAP, int_mask, int_en);
141 	if (ret < 0) {
142 		return ret;
143 	}
144 
145 	ret = adxl372_get_status(dev, &status1, &status2, NULL); /* Clear status */
146 	if (ret < 0) {
147 		return ret;
148 	}
149 
150 	ret = gpio_pin_interrupt_configure_dt(&cfg->interrupt,
151 					      GPIO_INT_EDGE_TO_ACTIVE);
152 	if (ret < 0) {
153 		return ret;
154 	}
155 
156 	return 0;
157 }
158 
adxl372_init_interrupt(const struct device * dev)159 int adxl372_init_interrupt(const struct device *dev)
160 {
161 	const struct adxl372_dev_config *cfg = dev->config;
162 	struct adxl372_data *drv_data = dev->data;
163 	int ret;
164 
165 	if (!gpio_is_ready_dt(&cfg->interrupt)) {
166 		LOG_ERR("GPIO port %s not ready", cfg->interrupt.port->name);
167 		return -EINVAL;
168 	}
169 
170 	ret = gpio_pin_configure_dt(&cfg->interrupt, GPIO_INPUT | GPIO_PUSH_PULL);
171 	if (ret < 0) {
172 		return ret;
173 	}
174 
175 	gpio_init_callback(&drv_data->gpio_cb,
176 			   adxl372_gpio_callback,
177 			   BIT(cfg->interrupt.pin));
178 
179 	ret = gpio_add_callback(cfg->interrupt.port, &drv_data->gpio_cb);
180 	if (ret < 0) {
181 		LOG_ERR("Failed to set gpio callback!");
182 		return ret;
183 	}
184 
185 	drv_data->dev = dev;
186 
187 #if defined(CONFIG_ADXL372_TRIGGER_OWN_THREAD)
188 	k_sem_init(&drv_data->gpio_sem, 0, K_SEM_MAX_LIMIT);
189 
190 	k_thread_create(&drv_data->thread, drv_data->thread_stack,
191 			CONFIG_ADXL372_THREAD_STACK_SIZE,
192 			adxl372_thread, drv_data,
193 			NULL, NULL, K_PRIO_COOP(CONFIG_ADXL372_THREAD_PRIORITY),
194 			0, K_NO_WAIT);
195 
196 	k_thread_name_set(&drv_data->thread, dev->name);
197 #elif defined(CONFIG_ADXL372_TRIGGER_GLOBAL_THREAD)
198 	drv_data->work.handler = adxl372_work_cb;
199 #endif
200 
201 	return 0;
202 }
203