1 /*
2  * Copyright (c) 2021 Aurelien Jarno
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/drivers/gpio.h>
9 #include <zephyr/drivers/i2c.h>
10 #include <zephyr/drivers/sensor.h>
11 #include <zephyr/sys/byteorder.h>
12 #include <zephyr/sys/__assert.h>
13 #include <zephyr/logging/log.h>
14 
15 LOG_MODULE_REGISTER(TI_HDC20XX, CONFIG_SENSOR_LOG_LEVEL);
16 
17 /* Register addresses */
18 #define TI_HDC20XX_REG_TEMP		0x00
19 #define TI_HDC20XX_REG_HUMIDITY		0x02
20 #define TI_HDC20XX_REG_INT_EN		0x07
21 #define TI_HDC20XX_REG_CONFIG		0x0E
22 #define TI_HDC20XX_REG_MEAS_CFG		0x0F
23 #define TI_HDC20XX_REG_MANUFACTURER_ID	0xFC
24 #define TI_HDC20XX_REG_DEVICE_ID	0xFE
25 
26 /* Register values */
27 #define TI_HDC20XX_MANUFACTURER_ID	0x5449
28 #define TI_HDC20XX_DEVICE_ID		0x07D0
29 
30 /* Register bits */
31 #define TI_HDC20XX_BIT_INT_EN_DRDY_EN		0x80
32 #define TI_HDC20XX_BIT_CONFIG_SOFT_RES		0x80
33 #define TI_HDC20XX_BIT_CONFIG_DRDY_INT_EN	0x04
34 
35 /* Reset time: not in the datasheet, but found by trial and error */
36 #define TI_HDC20XX_RESET_TIME		K_MSEC(1)
37 
38 /* Conversion time for 14-bit resolution. Temperature needs 660us and humidity 610us */
39 #define TI_HDC20XX_CONVERSION_TIME      K_MSEC(2)
40 
41 /* Temperature and humidity scale and factors from the datasheet ("Register Maps" section) */
42 #define TI_HDC20XX_RH_SCALE		100U
43 #define TI_HDC20XX_TEMP_OFFSET		-2654208	/* = -40.5 * 2^16 */
44 #define TI_HDC20XX_TEMP_SCALE		165U
45 
46 struct ti_hdc20xx_config {
47 	struct i2c_dt_spec bus;
48 	struct gpio_dt_spec gpio_int;
49 };
50 
51 struct ti_hdc20xx_data {
52 	struct gpio_callback cb_int;
53 	struct k_sem sem_int;
54 
55 	uint16_t t_sample;
56 	uint16_t rh_sample;
57 };
58 
ti_hdc20xx_int_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)59 static void ti_hdc20xx_int_callback(const struct device *dev,
60 				    struct gpio_callback *cb, uint32_t pins)
61 {
62 	struct ti_hdc20xx_data *data = CONTAINER_OF(cb, struct ti_hdc20xx_data, cb_int);
63 
64 	ARG_UNUSED(pins);
65 
66 	k_sem_give(&data->sem_int);
67 }
68 
ti_hdc20xx_sample_fetch(const struct device * dev,enum sensor_channel chan)69 static int ti_hdc20xx_sample_fetch(const struct device *dev,
70 				   enum sensor_channel chan)
71 {
72 	const struct ti_hdc20xx_config *config = dev->config;
73 	struct ti_hdc20xx_data *data = dev->data;
74 	uint16_t buf[2];
75 	int rc;
76 
77 	__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL);
78 
79 	/* start conversion of both temperature and humidity with the default accuracy (14 bits) */
80 	rc = i2c_reg_write_byte_dt(&config->bus, TI_HDC20XX_REG_MEAS_CFG, 0x01);
81 	if (rc < 0) {
82 		LOG_ERR("Failed to write measurement configuration register");
83 		return rc;
84 	}
85 
86 	/* wait for the conversion to finish */
87 	if (config->gpio_int.port) {
88 		k_sem_take(&data->sem_int, K_FOREVER);
89 	} else {
90 		k_sleep(TI_HDC20XX_CONVERSION_TIME);
91 	}
92 
93 	/* temperature and humidity registers are consecutive, read them in the same burst */
94 	rc = i2c_burst_read_dt(&config->bus, TI_HDC20XX_REG_TEMP, (uint8_t *)buf, sizeof(buf));
95 	if (rc < 0) {
96 		LOG_ERR("Failed to read sample data");
97 		return rc;
98 	}
99 
100 	data->t_sample = sys_le16_to_cpu(buf[0]);
101 	data->rh_sample = sys_le16_to_cpu(buf[1]);
102 
103 	return 0;
104 }
105 
106 
ti_hdc20xx_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)107 static int ti_hdc20xx_channel_get(const struct device *dev,
108 				  enum sensor_channel chan,
109 				  struct sensor_value *val)
110 {
111 	struct ti_hdc20xx_data *data = dev->data;
112 	int32_t tmp;
113 
114 	/* See datasheet "Register Maps" section for more details on processing sample data. */
115 	switch (chan) {
116 	case SENSOR_CHAN_AMBIENT_TEMP:
117 		/* val = -40.5 + 165 * sample / 2^16 */
118 		tmp = data->t_sample * TI_HDC20XX_TEMP_SCALE + TI_HDC20XX_TEMP_OFFSET;
119 		val->val1 = tmp >> 16;
120 		/* x * 1000000 / 2^16 = x * 15625 / 2^10 */
121 		val->val2 = ((tmp & 0xFFFF) * 15625U) >> 10;
122 		break;
123 	case SENSOR_CHAN_HUMIDITY:
124 		/* val = 100 * sample / 2^16 */
125 		tmp = data->rh_sample * TI_HDC20XX_RH_SCALE;
126 		val->val1 = tmp >> 16;
127 		/* x * 1000000 / 2^16 = x * 15625 / 2^10 */
128 		val->val2 = ((tmp & 0xFFFF) * 15625U) >> 10;
129 		break;
130 	default:
131 		return -ENOTSUP;
132 	}
133 
134 	return 0;
135 }
136 
137 static const struct sensor_driver_api ti_hdc20xx_api_funcs = {
138 	.sample_fetch = ti_hdc20xx_sample_fetch,
139 	.channel_get = ti_hdc20xx_channel_get,
140 };
141 
ti_hdc20xx_reset(const struct device * dev)142 static int ti_hdc20xx_reset(const struct device *dev)
143 {
144 	const struct ti_hdc20xx_config *config = dev->config;
145 	int rc;
146 
147 	rc = i2c_reg_write_byte_dt(&config->bus, TI_HDC20XX_REG_CONFIG,
148 				   TI_HDC20XX_BIT_CONFIG_SOFT_RES);
149 	if (rc < 0) {
150 		LOG_ERR("Failed to soft-reset device");
151 		return rc;
152 	}
153 	k_sleep(TI_HDC20XX_RESET_TIME);
154 
155 	return 0;
156 }
157 
ti_hdc20xx_init(const struct device * dev)158 static int ti_hdc20xx_init(const struct device *dev)
159 {
160 	const struct ti_hdc20xx_config *config = dev->config;
161 	struct ti_hdc20xx_data *data = dev->data;
162 	uint16_t buf[2];
163 	int rc;
164 
165 	if (!device_is_ready(config->bus.bus)) {
166 		LOG_ERR("I2C bus %s not ready", config->bus.bus->name);
167 		return -ENODEV;
168 	}
169 
170 	/* manufacturer and device ID registers are consecutive, read them in the same burst */
171 	rc = i2c_burst_read_dt(&config->bus, TI_HDC20XX_REG_MANUFACTURER_ID,
172 			       (uint8_t *)buf, sizeof(buf));
173 	if (rc < 0) {
174 		LOG_ERR("Failed to read manufacturer and device IDs");
175 		return rc;
176 	}
177 
178 	if (sys_le16_to_cpu(buf[0]) != TI_HDC20XX_MANUFACTURER_ID) {
179 		LOG_ERR("Failed to get correct manufacturer ID");
180 		return -EINVAL;
181 	}
182 	if (sys_le16_to_cpu(buf[1]) != TI_HDC20XX_DEVICE_ID) {
183 		LOG_ERR("Unsupported device ID");
184 		return -EINVAL;
185 	}
186 
187 	/* Soft-reset the device to bring all registers in a known and consistent state */
188 	rc = ti_hdc20xx_reset(dev);
189 	if (rc < 0) {
190 		return rc;
191 	}
192 
193 	/* Configure the interrupt GPIO if available */
194 	if (config->gpio_int.port) {
195 		if (!gpio_is_ready_dt(&config->gpio_int)) {
196 			LOG_ERR("Cannot get pointer to gpio interrupt device");
197 			return -ENODEV;
198 		}
199 
200 		rc = gpio_pin_configure_dt(&config->gpio_int, GPIO_INPUT);
201 		if (rc) {
202 			LOG_ERR("Failed to configure interrupt pin");
203 			return rc;
204 		}
205 
206 		gpio_init_callback(&data->cb_int, ti_hdc20xx_int_callback,
207 				   BIT(config->gpio_int.pin));
208 
209 		rc = gpio_add_callback(config->gpio_int.port, &data->cb_int);
210 		if (rc) {
211 			LOG_ERR("Failed to set interrupt callback");
212 			return rc;
213 		}
214 
215 		rc = gpio_pin_interrupt_configure_dt(&config->gpio_int, GPIO_INT_EDGE_TO_ACTIVE);
216 		if (rc) {
217 			LOG_ERR("Failed to configure interrupt");
218 			return rc;
219 		}
220 
221 		/* Initialize the semaphore */
222 		k_sem_init(&data->sem_int, 0, K_SEM_MAX_LIMIT);
223 
224 		/* Enable the data ready interrupt */
225 		rc = i2c_reg_write_byte_dt(&config->bus, TI_HDC20XX_REG_INT_EN,
226 					   TI_HDC20XX_BIT_INT_EN_DRDY_EN);
227 		if (rc) {
228 			LOG_ERR("Failed to enable the data ready interrupt");
229 			return rc;
230 		}
231 
232 		/* Enable the interrupt pin with level sensitive active low polarity */
233 		rc = i2c_reg_write_byte_dt(&config->bus, TI_HDC20XX_REG_CONFIG,
234 					   TI_HDC20XX_BIT_CONFIG_DRDY_INT_EN);
235 		if (rc) {
236 			LOG_ERR("Failed to enable the interrupt pin");
237 			return rc;
238 		}
239 	}
240 
241 	return 0;
242 }
243 
244 /* Main instantiation macro */
245 #define TI_HDC20XX_DEFINE(inst, compat)							\
246 	static struct ti_hdc20xx_data ti_hdc20xx_data_##compat##inst;			\
247 	static const struct ti_hdc20xx_config ti_hdc20xx_config_##compat##inst = {	\
248 		.bus = I2C_DT_SPEC_GET(DT_INST(inst, compat)),				\
249 		.gpio_int = GPIO_DT_SPEC_GET_OR(DT_INST(inst, compat), int_gpios, {0}),	\
250 	};										\
251 	DEVICE_DT_DEFINE(DT_INST(inst, compat),						\
252 			ti_hdc20xx_init,						\
253 			NULL,								\
254 			&ti_hdc20xx_data_##compat##inst,				\
255 			&ti_hdc20xx_config_##compat##inst,				\
256 			POST_KERNEL,							\
257 			CONFIG_SENSOR_INIT_PRIORITY,					\
258 			&ti_hdc20xx_api_funcs);
259 
260 /* Create the struct device for every status "okay" node in the devicetree. */
261 #define TI_HDC20XX_FOREACH_STATUS_OKAY(compat, fn)	\
262 	COND_CODE_1(DT_HAS_COMPAT_STATUS_OKAY(compat),	\
263 		    (UTIL_CAT(DT_FOREACH_OKAY_INST_,	\
264 			      compat)(fn)),		\
265 		    ())
266 
267 /*
268  * HDC2010 Low-Power Humidity and Temperature Digital Sensors
269  */
270 #define TI_HDC2010_DEFINE(inst) TI_HDC20XX_DEFINE(inst, ti_hdc2010)
271 TI_HDC20XX_FOREACH_STATUS_OKAY(ti_hdc2010, TI_HDC2010_DEFINE)
272 
273 /*
274  * HDC2021 High-Accuracy, Low-Power Humidity and Temperature Sensor
275  * With Assembly Protection Cover
276  */
277 #define TI_HDC2021_DEFINE(inst) TI_HDC20XX_DEFINE(inst, ti_hdc2021)
278 TI_HDC20XX_FOREACH_STATUS_OKAY(ti_hdc2021, TI_HDC2021_DEFINE)
279 
280 /*
281  * HDC2022 High-Accuracy, Low-Power Humidity and Temperature Sensor
282  * With IP67 Rated Water and Dust Protection Cover
283  */
284 #define TI_HDC2022_DEFINE(inst) TI_HDC20XX_DEFINE(inst, ti_hdc2022)
285 TI_HDC20XX_FOREACH_STATUS_OKAY(ti_hdc2022, TI_HDC2022_DEFINE)
286 
287 /*
288  * HDC2080 Low-Power Humidity and Temperature Digital Sensor
289  */
290 #define TI_HDC2080_DEFINE(inst) TI_HDC20XX_DEFINE(inst, ti_hdc2080)
291 TI_HDC20XX_FOREACH_STATUS_OKAY(ti_hdc2080, TI_HDC2080_DEFINE)
292