1 /*
2  * Copyright (c) 2025 Tobias Meyer <tobiuhg@gmail.com>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT ti_tmp11x
8 
9 #include <zephyr/kernel.h>
10 #include <zephyr/drivers/sensor.h>
11 #include <zephyr/drivers/gpio.h>
12 #include <zephyr/logging/log.h>
13 
14 #include "tmp11x.h"
15 
16 LOG_MODULE_DECLARE(TMP11X, CONFIG_SENSOR_LOG_LEVEL);
17 
18 /**
19  * tmp11x_trigger_set - link external trigger to threshold event
20  */
tmp11x_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)21 int tmp11x_trigger_set(const struct device *dev,
22 		       const struct sensor_trigger *trig,
23 		       sensor_trigger_handler_t handler)
24 {
25 	struct tmp11x_data *data = dev->data;
26 	const struct tmp11x_dev_config *config = dev->config;
27 
28 	if (!config->alert_gpio.port) {
29 		return -ENOTSUP;
30 	}
31 
32 	if (trig->chan != SENSOR_CHAN_ALL &&
33 	    trig->chan != SENSOR_CHAN_AMBIENT_TEMP) {
34 		LOG_ERR("Unsupported sensor trigger channel %d", trig->chan);
35 		return -ENOTSUP;
36 	}
37 
38 	if (trig->type != SENSOR_TRIG_THRESHOLD) {
39 		LOG_ERR("Unsupported sensor trigger type %d", trig->type);
40 		return -ENOTSUP;
41 	}
42 
43 	data->alert_handler = handler;
44 	data->alert_trigger = trig;
45 
46 	return 0;
47 }
48 
49 /**
50  * tmp11x_handle_interrupt - handle the alert event
51  * read status and call handler if registered
52  */
tmp11x_handle_interrupt(const struct device * dev)53 static void tmp11x_handle_interrupt(const struct device *dev)
54 {
55 	struct tmp11x_data *data = dev->data;
56 	const struct tmp11x_dev_config *cfg = dev->config;
57 	uint16_t config_reg;
58 	int ret;
59 
60 	/* Read configuration register to check alert status */
61 	ret = tmp11x_reg_read(dev, TMP11X_REG_CFGR, &config_reg);
62 	if (ret < 0) {
63 		LOG_ERR("Failed to read config register: %d", ret);
64 		goto re_enable;
65 	}
66 
67 	/* Call the user's alert handler if registered */
68 	if (data->alert_handler != NULL) {
69 		data->alert_handler(dev, data->alert_trigger);
70 	}
71 
72 re_enable:
73 	/* Re-enable interrupt - TMP11X alert pin is level-triggered */
74 	gpio_pin_interrupt_configure_dt(&cfg->alert_gpio, GPIO_INT_LEVEL_ACTIVE);
75 }
76 
tmp11x_gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)77 static void tmp11x_gpio_callback(const struct device *dev,
78 				 struct gpio_callback *cb, uint32_t pins)
79 {
80 	struct tmp11x_data *data = CONTAINER_OF(cb, struct tmp11x_data, alert_cb);
81 	const struct tmp11x_dev_config *cfg = data->dev->config;
82 
83 	ARG_UNUSED(pins);
84 
85 	/* Disable interrupt to avoid retriggering */
86 	gpio_pin_interrupt_configure_dt(&cfg->alert_gpio, GPIO_INT_DISABLE);
87 
88 #if defined(CONFIG_TMP11X_TRIGGER_OWN_THREAD)
89 	k_sem_give(&data->gpio_sem);
90 #elif defined(CONFIG_TMP11X_TRIGGER_GLOBAL_THREAD)
91 	k_work_submit(&data->work);
92 #endif /* CONFIG_TMP11X_TRIGGER_OWN_THREAD */
93 }
94 
95 #ifdef CONFIG_TMP11X_TRIGGER_OWN_THREAD
tmp11x_thread(void * p1,void * p2,void * p3)96 static void tmp11x_thread(void *p1, void *p2, void *p3)
97 {
98 	ARG_UNUSED(p2);
99 	ARG_UNUSED(p3);
100 
101 	struct tmp11x_data *data = p1;
102 
103 	while (1) {
104 		k_sem_take(&data->gpio_sem, K_FOREVER);
105 		tmp11x_handle_interrupt(data->dev);
106 	}
107 }
108 #endif /* CONFIG_TMP11X_TRIGGER_OWN_THREAD */
109 
110 #ifdef CONFIG_TMP11X_TRIGGER_GLOBAL_THREAD
tmp11x_work_cb(struct k_work * work)111 static void tmp11x_work_cb(struct k_work *work)
112 {
113 	struct tmp11x_data *data = CONTAINER_OF(work, struct tmp11x_data, work);
114 
115 	tmp11x_handle_interrupt(data->dev);
116 }
117 #endif /* CONFIG_TMP11X_TRIGGER_GLOBAL_THREAD */
118 
tmp11x_init_interrupt(const struct device * dev)119 int tmp11x_init_interrupt(const struct device *dev)
120 {
121 	struct tmp11x_data *data = dev->data;
122 	const struct tmp11x_dev_config *cfg = dev->config;
123 	int ret;
124 
125 	/* Check if alert GPIO is configured in device tree */
126 	if (!cfg->alert_gpio.port) {
127 		LOG_DBG("%s: Alert GPIO not configured", dev->name);
128 		return 0;
129 	}
130 
131 	if (!gpio_is_ready_dt(&cfg->alert_gpio)) {
132 		LOG_ERR("%s: Alert GPIO controller not ready", dev->name);
133 		return -ENODEV;
134 	}
135 
136 #if defined(CONFIG_TMP11X_TRIGGER_OWN_THREAD)
137 	k_sem_init(&data->gpio_sem, 0, K_SEM_MAX_LIMIT);
138 
139 	k_thread_create(&data->thread, data->thread_stack, CONFIG_TMP11X_THREAD_STACK_SIZE,
140 			tmp11x_thread, data, NULL, NULL, K_PRIO_COOP(CONFIG_TMP11X_THREAD_PRIORITY),
141 			0, K_NO_WAIT);
142 	k_thread_name_set(&data->thread, dev->name);
143 #elif defined(CONFIG_TMP11X_TRIGGER_GLOBAL_THREAD)
144 	data->work.handler = tmp11x_work_cb;
145 #endif /* CONFIG_TMP11X_TRIGGER_OWN_THREAD */
146 
147 	/* Configure GPIO as input */
148 	ret = gpio_pin_configure_dt(&cfg->alert_gpio, GPIO_INPUT);
149 	if (ret < 0) {
150 		LOG_ERR("%s: Failed to configure alert GPIO", dev->name);
151 		return ret;
152 	}
153 
154 	/* Initialize GPIO callback */
155 	gpio_init_callback(&data->alert_cb, tmp11x_gpio_callback, BIT(cfg->alert_gpio.pin));
156 
157 	/* Add callback to GPIO controller */
158 	ret = gpio_add_callback(cfg->alert_gpio.port, &data->alert_cb);
159 	if (ret < 0) {
160 		LOG_ERR("%s: Failed to add alert GPIO callback", dev->name);
161 		return ret;
162 	}
163 	/* Enable interrupt - TMP11X alert pin is level-triggered */
164 	ret = gpio_pin_interrupt_configure_dt(&cfg->alert_gpio, GPIO_INT_EDGE_TO_ACTIVE);
165 	if (ret < 0) {
166 		LOG_ERR("%s: Failed to configure alert pin interrupt", dev->name);
167 		return ret;
168 	}
169 
170 	LOG_DBG("%s: Alert pin initialized successfully", dev->name);
171 	return 0;
172 }
173