1 /*
2 * Copyright (c) 2016 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/device.h>
8 #include <zephyr/sys/util.h>
9 #include <zephyr/kernel.h>
10 #include <zephyr/drivers/sensor.h>
11
12 #include "sht3xd.h"
13
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_DECLARE(SHT3XD, CONFIG_SENSOR_LOG_LEVEL);
16
sht3xd_temp_processed_to_raw(const struct sensor_value * val)17 static uint16_t sht3xd_temp_processed_to_raw(const struct sensor_value *val)
18 {
19 uint64_t uval;
20
21 /* ret = (val + 45) * (2^16 - 1) / 175 */
22 uval = (uint64_t)(val->val1 + 45) * 1000000U + val->val2;
23 return ((uval * 0xFFFF) / 175) / 1000000;
24 }
25
sht3xd_rh_processed_to_raw(const struct sensor_value * val)26 static int sht3xd_rh_processed_to_raw(const struct sensor_value *val)
27 {
28 uint64_t uval;
29
30 /* ret = val * (2^16 -1) / 100 */
31 uval = (uint64_t)val->val1 * 1000000U + val->val2;
32 return ((uval * 0xFFFF) / 100) / 1000000;
33 }
34
sht3xd_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)35 int sht3xd_attr_set(const struct device *dev,
36 enum sensor_channel chan,
37 enum sensor_attribute attr,
38 const struct sensor_value *val)
39 {
40 struct sht3xd_data *data = dev->data;
41 uint16_t set_cmd, clear_cmd, reg_val, temp, rh;
42
43 if (attr == SENSOR_ATTR_LOWER_THRESH) {
44 if (chan == SENSOR_CHAN_AMBIENT_TEMP) {
45 data->t_low = sht3xd_temp_processed_to_raw(val);
46 } else if (chan == SENSOR_CHAN_HUMIDITY) {
47 data->rh_low = sht3xd_rh_processed_to_raw(val);
48 } else {
49 return -ENOTSUP;
50 }
51
52 set_cmd = SHT3XD_CMD_WRITE_TH_LOW_SET;
53 clear_cmd = SHT3XD_CMD_WRITE_TH_LOW_CLEAR;
54 temp = data->t_low;
55 rh = data->rh_low;
56 } else if (attr == SENSOR_ATTR_UPPER_THRESH) {
57 if (chan == SENSOR_CHAN_AMBIENT_TEMP) {
58 data->t_high = sht3xd_temp_processed_to_raw(val);
59 } else if (chan == SENSOR_CHAN_HUMIDITY) {
60 data->rh_high = sht3xd_rh_processed_to_raw(val);
61 } else {
62 return -ENOTSUP;
63 }
64
65 set_cmd = SHT3XD_CMD_WRITE_TH_HIGH_SET;
66 clear_cmd = SHT3XD_CMD_WRITE_TH_HIGH_CLEAR;
67 temp = data->t_high;
68 rh = data->rh_high;
69 } else {
70 return -ENOTSUP;
71 }
72
73 reg_val = (rh & 0xFE00) | ((temp & 0xFF80) >> 7);
74
75 if (sht3xd_write_reg(dev, set_cmd, reg_val) < 0 ||
76 sht3xd_write_reg(dev, clear_cmd, reg_val) < 0) {
77 LOG_DBG("Failed to write threshold value!");
78 return -EIO;
79 }
80
81 return 0;
82 }
83
setup_alert(const struct device * dev,bool enable)84 static inline void setup_alert(const struct device *dev,
85 bool enable)
86 {
87 const struct sht3xd_config *cfg =
88 (const struct sht3xd_config *)dev->config;
89 unsigned int flags = enable
90 ? GPIO_INT_EDGE_TO_ACTIVE
91 : GPIO_INT_DISABLE;
92
93 gpio_pin_interrupt_configure_dt(&cfg->alert_gpio, flags);
94 }
95
handle_alert(const struct device * dev)96 static inline void handle_alert(const struct device *dev)
97 {
98 setup_alert(dev, false);
99
100 #if defined(CONFIG_SHT3XD_TRIGGER_OWN_THREAD)
101 struct sht3xd_data *data = dev->data;
102
103 k_sem_give(&data->gpio_sem);
104 #elif defined(CONFIG_SHT3XD_TRIGGER_GLOBAL_THREAD)
105 struct sht3xd_data *data = dev->data;
106
107 k_work_submit(&data->work);
108 #endif
109 }
110
sht3xd_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)111 int sht3xd_trigger_set(const struct device *dev,
112 const struct sensor_trigger *trig,
113 sensor_trigger_handler_t handler)
114 {
115 struct sht3xd_data *data = dev->data;
116 const struct sht3xd_config *cfg =
117 (const struct sht3xd_config *)dev->config;
118
119 setup_alert(dev, false);
120
121 if (trig->type != SENSOR_TRIG_THRESHOLD) {
122 return -ENOTSUP;
123 }
124
125 data->handler = handler;
126 if (handler == NULL) {
127 return 0;
128 }
129
130 data->trigger = trig;
131
132 setup_alert(dev, true);
133
134 /* If ALERT is active we probably won't get the rising edge,
135 * so invoke the callback manually.
136 */
137 if (gpio_pin_get_dt(&cfg->alert_gpio)) {
138 handle_alert(dev);
139 }
140
141 return 0;
142 }
143
sht3xd_gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)144 static void sht3xd_gpio_callback(const struct device *dev,
145 struct gpio_callback *cb, uint32_t pins)
146 {
147 struct sht3xd_data *data =
148 CONTAINER_OF(cb, struct sht3xd_data, alert_cb);
149
150 handle_alert(data->dev);
151 }
152
sht3xd_thread_cb(const struct device * dev)153 static void sht3xd_thread_cb(const struct device *dev)
154 {
155 struct sht3xd_data *data = dev->data;
156
157 if (data->handler != NULL) {
158 data->handler(dev, data->trigger);
159 }
160
161 setup_alert(dev, true);
162 }
163
164 #ifdef CONFIG_SHT3XD_TRIGGER_OWN_THREAD
sht3xd_thread(void * p1,void * p2,void * p3)165 static void sht3xd_thread(void *p1, void *p2, void *p3)
166 {
167 ARG_UNUSED(p2);
168 ARG_UNUSED(p3);
169
170 struct sht3xd_data *data = p1;
171
172 while (1) {
173 k_sem_take(&data->gpio_sem, K_FOREVER);
174 sht3xd_thread_cb(data->dev);
175 }
176 }
177 #endif
178
179 #ifdef CONFIG_SHT3XD_TRIGGER_GLOBAL_THREAD
sht3xd_work_cb(struct k_work * work)180 static void sht3xd_work_cb(struct k_work *work)
181 {
182 struct sht3xd_data *data =
183 CONTAINER_OF(work, struct sht3xd_data, work);
184
185 sht3xd_thread_cb(data->dev);
186 }
187 #endif
188
sht3xd_init_interrupt(const struct device * dev)189 int sht3xd_init_interrupt(const struct device *dev)
190 {
191 struct sht3xd_data *data = dev->data;
192 const struct sht3xd_config *cfg = dev->config;
193 int rc;
194
195 if (!gpio_is_ready_dt(&cfg->alert_gpio)) {
196 LOG_ERR("GPIO device not ready");
197 return -ENODEV;
198 }
199
200 rc = gpio_pin_configure_dt(&cfg->alert_gpio, GPIO_INPUT);
201 if (rc != 0) {
202 LOG_DBG("Failed to configure alert pin %u!", cfg->alert_gpio.pin);
203 return -EIO;
204 }
205
206 gpio_init_callback(&data->alert_cb, sht3xd_gpio_callback,
207 BIT(cfg->alert_gpio.pin));
208 rc = gpio_add_callback(cfg->alert_gpio.port, &data->alert_cb);
209 if (rc < 0) {
210 LOG_DBG("Failed to set gpio callback!");
211 return -EIO;
212 }
213
214 /* set alert thresholds to match measurement ranges */
215 data->t_low = 0U;
216 data->rh_low = 0U;
217 data->t_high = 0xFFFF;
218 data->rh_high = 0xFFFF;
219 if (sht3xd_write_reg(dev, SHT3XD_CMD_WRITE_TH_HIGH_SET, 0xFFFF)
220 < 0) {
221 LOG_DBG("Failed to write threshold high set value!");
222 return -EIO;
223 }
224
225 if (sht3xd_write_reg(dev, SHT3XD_CMD_WRITE_TH_HIGH_CLEAR,
226 0xFFFF) < 0) {
227 LOG_DBG("Failed to write threshold high clear value!");
228 return -EIO;
229 }
230
231 if (sht3xd_write_reg(dev, SHT3XD_CMD_WRITE_TH_LOW_SET, 0) < 0) {
232 LOG_DBG("Failed to write threshold low set value!");
233 return -EIO;
234 }
235
236 if (sht3xd_write_reg(dev, SHT3XD_CMD_WRITE_TH_LOW_SET, 0) < 0) {
237 LOG_DBG("Failed to write threshold low clear value!");
238 return -EIO;
239 }
240
241 #if defined(CONFIG_SHT3XD_TRIGGER_OWN_THREAD)
242 k_sem_init(&data->gpio_sem, 0, K_SEM_MAX_LIMIT);
243
244 k_thread_create(&data->thread, data->thread_stack,
245 CONFIG_SHT3XD_THREAD_STACK_SIZE,
246 sht3xd_thread, data,
247 NULL, NULL, K_PRIO_COOP(CONFIG_SHT3XD_THREAD_PRIORITY),
248 0, K_NO_WAIT);
249 #elif defined(CONFIG_SHT3XD_TRIGGER_GLOBAL_THREAD)
250 data->work.handler = sht3xd_work_cb;
251 #endif
252
253 return 0;
254 }
255