1 /*
2  * Copyright (c) 2025 Würth Elektronik eiSos GmbH & Co. KG
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT we_wsen_tids_2521020222501
8 
9 #include <stdlib.h>
10 
11 #include <zephyr/logging/log.h>
12 
13 #include "wsen_tids_2521020222501.h"
14 
15 LOG_MODULE_DECLARE(WSEN_TIDS_2521020222501, CONFIG_SENSOR_LOG_LEVEL);
16 
17 /* Enable/disable interrupt handling */
tids_2521020222501_setup_interrupt(const struct device * dev,bool enable)18 static inline void tids_2521020222501_setup_interrupt(const struct device *dev, bool enable)
19 {
20 	const struct tids_2521020222501_config *cfg = dev->config;
21 	unsigned int flags = enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE;
22 
23 	gpio_pin_interrupt_configure_dt(&cfg->interrupt_gpio, flags);
24 }
25 
26 /*
27  * Is called when an interrupt has occurred.
28  */
tids_2521020222501_handle_interrupt(const struct device * dev)29 static void tids_2521020222501_handle_interrupt(const struct device *dev)
30 {
31 	struct tids_2521020222501_data *data = dev->data;
32 
33 	/* Disable interrupt handling until the interrupt has been processed */
34 	tids_2521020222501_setup_interrupt(dev, false);
35 
36 #if defined(CONFIG_WSEN_TIDS_2521020222501_TRIGGER_OWN_THREAD)
37 	k_sem_give(&data->sem);
38 #elif defined(CONFIG_WSEN_TIDS_2521020222501_TRIGGER_GLOBAL_THREAD)
39 	k_work_submit(&data->work);
40 #endif
41 }
42 
43 /*
44  * Calls trigger handles.
45  */
tids_2521020222501_process_interrupt(const struct device * dev)46 static void tids_2521020222501_process_interrupt(const struct device *dev)
47 {
48 	struct tids_2521020222501_data *data = dev->data;
49 
50 	if (data->temperature_high_handler != NULL || data->temperature_low_handler != NULL) {
51 		/*
52 		 * Read the sensor's status register - this also causes the interrupt pin
53 		 * to be de-asserted
54 		 */
55 		TIDS_status_t status;
56 
57 		if (TIDS_getStatusRegister(&data->sensor_interface, &status) != WE_SUCCESS) {
58 			LOG_ERR("Failed to read status register");
59 			return;
60 		}
61 
62 		if (data->temperature_high_handler != NULL &&
63 		    status.upperLimitExceeded == TIDS_enable) {
64 			data->temperature_high_handler(dev, data->temperature_high_trigger);
65 		} else if (data->temperature_low_handler != NULL &&
66 			   status.lowerLimitExceeded == TIDS_enable) {
67 			data->temperature_low_handler(dev, data->temperature_low_trigger);
68 		}
69 	}
70 
71 	tids_2521020222501_setup_interrupt(dev, true);
72 }
73 
74 /* Enables/disables processing of the "threshold exceeded" interrupt. */
tids_2521020222501_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)75 int tids_2521020222501_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
76 				   sensor_trigger_handler_t handler)
77 {
78 	struct tids_2521020222501_data *data = dev->data;
79 	const struct tids_2521020222501_config *cfg = dev->config;
80 
81 	switch (trig->chan) {
82 	case SENSOR_CHAN_ALL:
83 	case SENSOR_CHAN_AMBIENT_TEMP:
84 		break;
85 	default:
86 		LOG_ERR("Unsupported sensor trigger");
87 		return -ENOTSUP;
88 	}
89 
90 	struct sensor_value interruptOFF = {.val1 = -40, .val2 = -320000};
91 	struct sensor_value threshold;
92 
93 	switch ((int)trig->type) {
94 	case SENSOR_TRIG_WSEN_TIDS_2521020222501_THRESHOLD_LOWER: {
95 
96 		threshold.val1 = data->sensor_low_threshold / 1000;
97 		threshold.val2 = ((int32_t)data->sensor_low_threshold % 1000) * (1000000 / 1000);
98 
99 		if (tids_2521020222501_threshold_lower_set(
100 			    dev, (handler == NULL) ? &interruptOFF : &threshold) < 0) {
101 			LOG_ERR("Failed to set low temp threshold");
102 		}
103 		data->temperature_low_handler = handler;
104 		data->temperature_low_trigger = trig;
105 		break;
106 	}
107 	case SENSOR_TRIG_WSEN_TIDS_2521020222501_THRESHOLD_UPPER: {
108 
109 		threshold.val1 = data->sensor_high_threshold / 1000;
110 		threshold.val2 = ((int32_t)data->sensor_high_threshold % 1000) * (1000000 / 1000);
111 
112 		if (tids_2521020222501_threshold_upper_set(
113 			    dev, (handler == NULL) ? &interruptOFF : &threshold) < 0) {
114 			LOG_ERR("Failed to set high temp threshold");
115 		}
116 		data->temperature_high_handler = handler;
117 		data->temperature_high_trigger = trig;
118 		break;
119 	}
120 	default:
121 		LOG_ERR("Unsupported sensor trigger");
122 		return -ENOTSUP;
123 	}
124 
125 	tids_2521020222501_setup_interrupt(dev, data->temperature_high_handler ||
126 							data->temperature_low_handler);
127 
128 	if (gpio_pin_get_dt(&cfg->interrupt_gpio) > 0) {
129 		tids_2521020222501_handle_interrupt(dev);
130 	}
131 
132 	return 0;
133 }
134 
tids_2521020222501_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)135 static void tids_2521020222501_callback(const struct device *dev, struct gpio_callback *cb,
136 					uint32_t pins)
137 {
138 	struct tids_2521020222501_data *data =
139 		CONTAINER_OF(cb, struct tids_2521020222501_data, interrupt_cb);
140 
141 	ARG_UNUSED(pins);
142 
143 	tids_2521020222501_handle_interrupt(data->dev);
144 }
145 
146 #ifdef CONFIG_WSEN_TIDS_2521020222501_TRIGGER_OWN_THREAD
tids_2521020222501_thread(void * p1,void * p2,void * p3)147 static void tids_2521020222501_thread(void *p1, void *p2, void *p3)
148 {
149 	ARG_UNUSED(p2);
150 	ARG_UNUSED(p3);
151 
152 	struct tids_2521020222501_data *tids_2521020222501 = p1;
153 
154 	while (true) {
155 		k_sem_take(&tids_2521020222501->sem, K_FOREVER);
156 		tids_2521020222501_process_interrupt(tids_2521020222501->dev);
157 	}
158 }
159 #endif /* CONFIG_WSEN_TIDS_2521020222501_TRIGGER_OWN_THREAD */
160 
161 #ifdef CONFIG_WSEN_TIDS_2521020222501_TRIGGER_GLOBAL_THREAD
tids_2521020222501_work_cb(struct k_work * work)162 static void tids_2521020222501_work_cb(struct k_work *work)
163 {
164 	struct tids_2521020222501_data *tids_2521020222501 =
165 		CONTAINER_OF(work, struct tids_2521020222501_data, work);
166 
167 	tids_2521020222501_process_interrupt(tids_2521020222501->dev);
168 }
169 #endif /* CONFIG_WSEN_TIDS_2521020222501_TRIGGER_GLOBAL_THREAD */
170 
tids_2521020222501_threshold_upper_set(const struct device * dev,const struct sensor_value * thresh_value)171 int tids_2521020222501_threshold_upper_set(const struct device *dev,
172 					   const struct sensor_value *thresh_value)
173 {
174 	struct tids_2521020222501_data *data = dev->data;
175 	int32_t thresh = thresh_value->val1 * 1000 + thresh_value->val2 / 1000;
176 
177 	if (TIDS_setTempHighLimit(&data->sensor_interface, thresh) != WE_SUCCESS) {
178 		LOG_ERR("Failed to set high temperature threshold.");
179 		return -EIO;
180 	}
181 
182 	data->sensor_high_threshold = thresh;
183 
184 	return 0;
185 }
186 
tids_2521020222501_threshold_upper_get(const struct device * dev,struct sensor_value * thresh_value)187 int tids_2521020222501_threshold_upper_get(const struct device *dev,
188 					   struct sensor_value *thresh_value)
189 {
190 	struct tids_2521020222501_data *data = dev->data;
191 	int32_t thresh;
192 
193 	if (TIDS_getTempHighLimit(&data->sensor_interface, &thresh) != WE_SUCCESS) {
194 		LOG_ERR("Failed to get high temperature threshold.");
195 		return -EIO;
196 	}
197 
198 	thresh_value->val1 = thresh / 1000;
199 	thresh_value->val2 = (thresh % 1000) * (1000000 / 1000);
200 
201 	return 0;
202 }
203 
tids_2521020222501_threshold_lower_set(const struct device * dev,const struct sensor_value * thresh_value)204 int tids_2521020222501_threshold_lower_set(const struct device *dev,
205 					   const struct sensor_value *thresh_value)
206 {
207 	struct tids_2521020222501_data *data = dev->data;
208 	int32_t thresh = thresh_value->val1 * 1000 + thresh_value->val2 / 1000;
209 
210 	if (TIDS_setTempLowLimit(&data->sensor_interface, thresh) != WE_SUCCESS) {
211 		LOG_ERR("Failed to set low temperature threshold.");
212 		return -EIO;
213 	}
214 
215 	data->sensor_low_threshold = thresh;
216 
217 	return 0;
218 }
219 
tids_2521020222501_threshold_lower_get(const struct device * dev,struct sensor_value * thresh_value)220 int tids_2521020222501_threshold_lower_get(const struct device *dev,
221 					   struct sensor_value *thresh_value)
222 {
223 	struct tids_2521020222501_data *data = dev->data;
224 	int32_t thresh;
225 
226 	if (TIDS_getTempLowLimit(&data->sensor_interface, &thresh) != WE_SUCCESS) {
227 		LOG_ERR("Failed to get low temperature threshold.");
228 		return -EIO;
229 	}
230 
231 	thresh_value->val1 = thresh / 1000;
232 	thresh_value->val2 = (thresh % 1000) * (1000000 / 1000);
233 
234 	return 0;
235 }
236 
tids_2521020222501_init_interrupt(const struct device * dev)237 int tids_2521020222501_init_interrupt(const struct device *dev)
238 {
239 	struct tids_2521020222501_data *data = dev->data;
240 	const struct tids_2521020222501_config *cfg = dev->config;
241 	struct sensor_value upper_limit, lower_limit;
242 
243 	if (cfg->interrupt_gpio.port == NULL) {
244 		LOG_ERR("interrupt-gpios is not defined in the device tree.");
245 		return -EINVAL;
246 	}
247 
248 	if (!gpio_is_ready_dt(&cfg->interrupt_gpio)) {
249 		LOG_ERR("Device %s is not ready", cfg->interrupt_gpio.port->name);
250 		return -ENODEV;
251 	}
252 
253 	data->dev = dev;
254 
255 	/* Setup threshold gpio interrupt */
256 	if (gpio_pin_configure_dt(&cfg->interrupt_gpio, GPIO_INPUT) < 0) {
257 		LOG_ERR("Failed to configure %s.%02u", cfg->interrupt_gpio.port->name,
258 			cfg->interrupt_gpio.pin);
259 		return -EIO;
260 	}
261 
262 	gpio_init_callback(&data->interrupt_cb, tids_2521020222501_callback,
263 			   BIT(cfg->interrupt_gpio.pin));
264 
265 	if (gpio_add_callback(cfg->interrupt_gpio.port, &data->interrupt_cb) < 0) {
266 		LOG_ERR("Failed to set gpio callback.");
267 		return -EIO;
268 	}
269 
270 	/*
271 	 * Enable interrupt on high/low temperature (interrupt generation is enabled if at
272 	 * least one threshold is non-zero)
273 	 */
274 
275 	upper_limit.val1 = cfg->high_threshold / 1000;
276 	upper_limit.val2 = ((int32_t)cfg->high_threshold % 1000) * (1000000 / 1000);
277 
278 	lower_limit.val1 = cfg->low_threshold / 1000;
279 	lower_limit.val2 = ((int32_t)cfg->low_threshold % 1000) * (1000000 / 1000);
280 
281 	if (tids_2521020222501_threshold_upper_set(dev, &upper_limit) < 0) {
282 		LOG_ERR("Failed to set upper threshold");
283 		return -EIO;
284 	}
285 
286 	if (tids_2521020222501_threshold_lower_set(dev, &lower_limit) < 0) {
287 		LOG_ERR("Failed to set lower threshold");
288 		return -EIO;
289 	}
290 
291 #if defined(CONFIG_WSEN_TIDS_2521020222501_TRIGGER_OWN_THREAD)
292 	k_sem_init(&data->sem, 0, K_SEM_MAX_LIMIT);
293 
294 	k_thread_create(&data->thread, data->thread_stack,
295 			CONFIG_WSEN_TIDS_2521020222501_THREAD_STACK_SIZE, tids_2521020222501_thread,
296 			data, NULL, NULL,
297 			K_PRIO_COOP(CONFIG_WSEN_TIDS_2521020222501_THREAD_PRIORITY), 0, K_NO_WAIT);
298 #elif defined(CONFIG_WSEN_TIDS_2521020222501_TRIGGER_GLOBAL_THREAD)
299 	data->work.handler = tids_2521020222501_work_cb;
300 #endif
301 
302 	return 0;
303 }
304