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