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