1 /*
2 * Copyright (c) 2022 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
8
9 #include <stdlib.h>
10
11 #include <zephyr/logging/log.h>
12
13 #include "wsen_tids.h"
14
15 LOG_MODULE_DECLARE(WSEN_TIDS, CONFIG_SENSOR_LOG_LEVEL);
16
17 #define THRESHOLD_TEMPERATURE2REGISTER_OFFSET (double)63.
18 #define THRESHOLD_TEMPERATURE2REGISTER_STEP (double)0.64
19
20 /* Enable/disable threshold interrupt handling */
tids_setup_threshold_interrupt(const struct device * dev,bool enable)21 static inline void tids_setup_threshold_interrupt(const struct device *dev, bool enable)
22 {
23 const struct tids_config *cfg = dev->config;
24 unsigned int flags = enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE;
25
26 gpio_pin_interrupt_configure_dt(&cfg->gpio_threshold, flags);
27 }
28
29 /*
30 * Is called when a "threshold exceeded" interrupt occurred. Triggers
31 * asynchronous processing of the interrupt in tids_process_threshold_interrupt().
32 */
tids_handle_threshold_interrupt(const struct device * dev)33 static void tids_handle_threshold_interrupt(const struct device *dev)
34 {
35 struct tids_data *data = dev->data;
36
37 /* Disable interrupt handling until the interrupt has been processed */
38 tids_setup_threshold_interrupt(dev, false);
39
40 #if defined(CONFIG_WSEN_TIDS_TRIGGER_OWN_THREAD)
41 k_sem_give(&data->threshold_sem);
42 #elif defined(CONFIG_WSEN_TIDS_TRIGGER_GLOBAL_THREAD)
43 k_work_submit(&data->work);
44 #endif
45 }
46
47 /*
48 * Is called after a "threshold exceeded" interrupt occurred.
49 * Checks the sensor's status register for the limit exceeded flags and
50 * calls the trigger handler if one of the flags is set.
51 */
tids_process_threshold_interrupt(const struct device * dev)52 static void tids_process_threshold_interrupt(const struct device *dev)
53 {
54 struct tids_data *data = dev->data;
55 TIDS_status_t status;
56
57 /*
58 * Read the sensor's status register - this also causes the interrupt pin
59 * to be de-asserted
60 */
61 if (TIDS_getStatusRegister(&data->sensor_interface, &status) != WE_SUCCESS) {
62 LOG_ERR("Failed to read status register");
63 return;
64 }
65
66 if (data->threshold_handler != NULL &&
67 (status.upperLimitExceeded != 0 || status.lowerLimitExceeded != 0)) {
68 data->threshold_handler(dev, data->threshold_trigger);
69 }
70
71 if (data->threshold_handler != NULL) {
72 tids_setup_threshold_interrupt(dev, true);
73 }
74 }
75
76 /* Enables/disables processing of the "threshold exceeded" interrupt. */
tids_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)77 int tids_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
78 sensor_trigger_handler_t handler)
79 {
80 struct tids_data *data = dev->data;
81 const struct tids_config *cfg = dev->config;
82
83 if (trig->type != SENSOR_TRIG_THRESHOLD) {
84 LOG_ERR("Unsupported sensor trigger");
85 return -ENOTSUP;
86 }
87
88 tids_setup_threshold_interrupt(dev, false);
89
90 data->threshold_handler = handler;
91 if (handler == NULL) {
92 return 0;
93 }
94
95 data->threshold_trigger = trig;
96
97 tids_setup_threshold_interrupt(dev, true);
98
99 /*
100 * If threshold interrupt is active we probably won't get the rising edge, so
101 * invoke the callback manually.
102 */
103 if (gpio_pin_get_dt(&cfg->gpio_threshold) > 0) {
104 tids_handle_threshold_interrupt(dev);
105 }
106
107 return 0;
108 }
109
tids_threshold_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)110 static void tids_threshold_callback(const struct device *dev, struct gpio_callback *cb,
111 uint32_t pins)
112 {
113 struct tids_data *data = CONTAINER_OF(cb, struct tids_data, threshold_cb);
114
115 ARG_UNUSED(pins);
116
117 tids_handle_threshold_interrupt(data->dev);
118 }
119
120 #ifdef CONFIG_WSEN_TIDS_TRIGGER_OWN_THREAD
tids_thread(struct tids_data * tids)121 static void tids_thread(struct tids_data *tids)
122 {
123 while (true) {
124 k_sem_take(&tids->threshold_sem, K_FOREVER);
125 tids_process_threshold_interrupt(tids->dev);
126 }
127 }
128 #endif /* CONFIG_WSEN_TIDS_TRIGGER_OWN_THREAD */
129
130 #ifdef CONFIG_WSEN_TIDS_TRIGGER_GLOBAL_THREAD
tids_work_cb(struct k_work * work)131 static void tids_work_cb(struct k_work *work)
132 {
133 struct tids_data *tids = CONTAINER_OF(work, struct tids_data, work);
134
135 tids_process_threshold_interrupt(tids->dev);
136 }
137 #endif /* CONFIG_WSEN_TIDS_TRIGGER_GLOBAL_THREAD */
138
tids_threshold_set(const struct device * dev,const struct sensor_value * thresh_value,bool upper)139 int tids_threshold_set(const struct device *dev, const struct sensor_value *thresh_value,
140 bool upper)
141 {
142 struct tids_data *data = dev->data;
143 double thresh = (sensor_value_to_double(thresh_value) / THRESHOLD_TEMPERATURE2REGISTER_STEP)
144 + THRESHOLD_TEMPERATURE2REGISTER_OFFSET;
145
146 if (thresh < 0) {
147 thresh = 0;
148 } else if (thresh > 255) {
149 thresh = 255;
150 }
151
152 if (upper) {
153 if (TIDS_setTempHighLimit(&data->sensor_interface, (uint8_t)thresh) != WE_SUCCESS) {
154 LOG_ERR("Failed to set high temperature threshold to %d.%d (%d).",
155 thresh_value->val1, abs(thresh_value->val2), (uint8_t)thresh);
156 return -EIO;
157 }
158 } else {
159 if (TIDS_setTempLowLimit(&data->sensor_interface, (uint8_t)thresh) != WE_SUCCESS) {
160 LOG_ERR("Failed to set low temperature threshold to %d.%d (%d).",
161 thresh_value->val1, abs(thresh_value->val2), (uint8_t)thresh);
162 return -EIO;
163 }
164 }
165
166 return 0;
167 }
168
tids_init_interrupt(const struct device * dev)169 int tids_init_interrupt(const struct device *dev)
170 {
171 struct tids_data *data = dev->data;
172 const struct tids_config *cfg = dev->config;
173 int status;
174 struct sensor_value upper_limit;
175 struct sensor_value lower_limit;
176
177 if (cfg->gpio_threshold.port == NULL) {
178 LOG_ERR("int-gpios is not defined in the device tree.");
179 return -EINVAL;
180 }
181
182 if (!gpio_is_ready_dt(&cfg->gpio_threshold)) {
183 LOG_ERR("Device %s is not ready", cfg->gpio_threshold.port->name);
184 return -ENODEV;
185 }
186
187 data->dev = dev;
188
189 /* Setup threshold gpio interrupt */
190 status = gpio_pin_configure_dt(&cfg->gpio_threshold, GPIO_INPUT);
191 if (status < 0) {
192 LOG_ERR("Failed to configure %s.%02u", cfg->gpio_threshold.port->name,
193 cfg->gpio_threshold.pin);
194 return status;
195 }
196
197 gpio_init_callback(&data->threshold_cb, tids_threshold_callback,
198 BIT(cfg->gpio_threshold.pin));
199
200 status = gpio_add_callback(cfg->gpio_threshold.port, &data->threshold_cb);
201 if (status < 0) {
202 LOG_ERR("Failed to set gpio callback.");
203 return status;
204 }
205
206 /*
207 * Enable interrupt on high/low temperature (interrupt generation is enabled if at
208 * least one threshold is non-zero)
209 */
210 upper_limit.val1 = cfg->high_threshold;
211 upper_limit.val2 = 0;
212 lower_limit.val1 = cfg->low_threshold;
213 lower_limit.val2 = 0;
214
215 status = tids_threshold_set(dev, &upper_limit, true);
216 if (status < 0) {
217 return status;
218 }
219
220 status = tids_threshold_set(dev, &lower_limit, false);
221 if (status < 0) {
222 return status;
223 }
224
225 #if defined(CONFIG_WSEN_TIDS_TRIGGER_OWN_THREAD)
226 k_sem_init(&data->threshold_sem, 0, K_SEM_MAX_LIMIT);
227
228 k_thread_create(&data->thread, data->thread_stack, CONFIG_WSEN_TIDS_THREAD_STACK_SIZE,
229 (k_thread_entry_t)tids_thread, data, NULL, NULL,
230 K_PRIO_COOP(CONFIG_WSEN_TIDS_THREAD_PRIORITY), 0, K_NO_WAIT);
231 #elif defined(CONFIG_WSEN_TIDS_TRIGGER_GLOBAL_THREAD)
232 data->work.handler = tids_work_cb;
233 #endif
234
235 tids_setup_threshold_interrupt(dev, true);
236
237 return 0;
238 }
239