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