1 /*
2  * Copyright (c) 2018 Alexander Wachter.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT ams_ens210
8 
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/sys/byteorder.h>
13 #include <zephyr/sys/util.h>
14 #include <zephyr/drivers/sensor.h>
15 #include <zephyr/sys/__assert.h>
16 #include <zephyr/logging/log.h>
17 #include "ens210.h"
18 
19 LOG_MODULE_REGISTER(ENS210, CONFIG_SENSOR_LOG_LEVEL);
20 
21 #ifdef CONFIG_ENS210_CRC_CHECK
ens210_crc7(uint32_t bitstream)22 static uint32_t ens210_crc7(uint32_t bitstream)
23 {
24 	uint32_t polynomial = (ENS210_CRC7_POLY << (ENS210_CRC7_DATA_WIDTH - 1));
25 	uint32_t bit = ENS210_CRC7_DATA_MSB << ENS210_CRC7_WIDTH;
26 	uint32_t val = (bitstream << ENS210_CRC7_WIDTH) | ENS210_CRC7_IVEC;
27 
28 	while (bit & (ENS210_CRC7_DATA_MASK << ENS210_CRC7_WIDTH)) {
29 		if (bit & val) {
30 			val ^= polynomial;
31 		}
32 
33 		bit >>= 1;
34 		polynomial >>= 1;
35 	}
36 
37 	return val;
38 }
39 #endif /* CONFIG_ENS210_CRC_CHECK */
40 
41 #if defined(CONFIG_ENS210_TEMPERATURE_SINGLE) \
42 		|| defined(CONFIG_ENS210_HUMIDITY_SINGLE)
ens210_measure(const struct device * dev,enum sensor_channel chan)43 static int ens210_measure(const struct device *dev, enum sensor_channel chan)
44 {
45 	struct ens210_data *drv_data = dev->data;
46 	const struct ens210_config *config = dev->config;
47 	uint8_t buf;
48 	int ret;
49 	const struct ens210_sens_start sense_start = {
50 		.t_start = ENS210_T_START && (chan == SENSOR_CHAN_ALL
51 				|| chan == SENSOR_CHAN_AMBIENT_TEMP),
52 		.h_start = ENS210_H_START && (chan == SENSOR_CHAN_ALL
53 				|| chan == SENSOR_CHAN_HUMIDITY)
54 	};
55 
56 	/* Start measuring */
57 	ret = i2c_reg_write_byte_dt(&config->i2c, ENS210_REG_SENS_START, *(uint8_t *)&sense_start);
58 
59 	if (ret < 0) {
60 		LOG_ERR("Failed to set SENS_START to 0x%x",
61 				*(uint8_t *)&sense_start);
62 		return -EIO;
63 	}
64 
65 	/* Wait for measurement to be completed */
66 	do {
67 		k_sleep(K_MSEC(2));
68 		ret = i2c_reg_read_byte_dt(&config->i2c, ENS210_REG_SENS_START, &buf);
69 
70 		if (ret < 0) {
71 			LOG_ERR("Failed to read SENS_STAT");
72 		}
73 	} while (buf & *(uint8_t *)&sense_start);
74 
75 	return ret;
76 }
77 #endif /* Single shot mode */
78 
ens210_sample_fetch(const struct device * dev,enum sensor_channel chan)79 static int ens210_sample_fetch(const struct device *dev,
80 			       enum sensor_channel chan)
81 {
82 	struct ens210_data *drv_data = dev->data;
83 	const struct ens210_config *config = dev->config;
84 	struct ens210_value_data data[2];
85 	int ret, cnt;
86 
87 #ifdef CONFIG_ENS210_CRC_CHECK
88 	uint32_t temp_valid, humidity_valid;
89 #endif /* CONFIG_ENS210_CRC_CHECK */
90 
91 	__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL
92 			|| chan == SENSOR_CHAN_AMBIENT_TEMP
93 			|| chan == SENSOR_CHAN_HUMIDITY);
94 
95 #if defined(CONFIG_ENS210_TEMPERATURE_SINGLE) \
96 		|| defined(CONFIG_ENS210_HUMIDITY_SINGLE)
97 	ret = ens210_measure(dev, chan);
98 	if (ret < 0) {
99 		LOG_ERR("Failed to measure");
100 		return ret;
101 	}
102 #endif /* Single shot mode */
103 
104 	for (cnt = 0; cnt <= CONFIG_ENS210_MAX_READ_RETRIES; cnt++) {
105 		ret = i2c_burst_read_dt(&config->i2c, ENS210_REG_T_VAL, (uint8_t *)&data,
106 					sizeof(data));
107 		if (ret < 0) {
108 			LOG_ERR("Failed to read data");
109 			continue;
110 		}
111 
112 		/* Get temperature value */
113 		if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_AMBIENT_TEMP) {
114 
115 			if (!data[0].valid) {
116 				LOG_WRN("Temperature not valid");
117 				continue;
118 			}
119 
120 #ifdef CONFIG_ENS210_CRC_CHECK
121 			temp_valid = data[0].val |
122 					(data[0].valid << (sizeof(data[0].val) * 8));
123 
124 			if (ens210_crc7(temp_valid) != data[0].crc7) {
125 				LOG_WRN("Temperature CRC error");
126 				continue;
127 			}
128 #endif /* CONFIG_ENS210_CRC_CHECK */
129 
130 			drv_data->temp = data[0];
131 		}
132 
133 		/* Get humidity value */
134 		if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_HUMIDITY) {
135 
136 			if (!data[1].valid) {
137 				LOG_WRN("Humidity not valid");
138 				continue;
139 			}
140 
141 #ifdef CONFIG_ENS210_CRC_CHECK
142 			humidity_valid = data[1].val |
143 					  (data[1].valid << (sizeof(data[1].val) * 8));
144 
145 			if (ens210_crc7(humidity_valid) != data[1].crc7) {
146 				LOG_WRN("Humidity CRC error");
147 				continue;
148 			}
149 #endif /* CONFIG_ENS210_CRC_CHECK */
150 
151 			drv_data->humidity = data[1];
152 		}
153 
154 		return 0;
155 	}
156 
157 	return -EIO;
158 }
159 
ens210_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)160 static int ens210_channel_get(const struct device *dev,
161 			      enum sensor_channel chan,
162 			      struct sensor_value *val)
163 {
164 	struct ens210_data *drv_data = dev->data;
165 	int32_t temp_frac;
166 	int32_t humidity_frac;
167 
168 	switch (chan) {
169 	case SENSOR_CHAN_AMBIENT_TEMP:
170 		/* Temperature is in 1/64 Kelvin. Subtract 273.15 for Celsius */
171 		temp_frac = sys_le16_to_cpu(drv_data->temp.val) * (1000000 / 64);
172 		temp_frac -= 273150000;
173 
174 		val->val1 = temp_frac / 1000000;
175 		val->val2 = temp_frac % 1000000;
176 		break;
177 	case  SENSOR_CHAN_HUMIDITY:
178 		humidity_frac = sys_le16_to_cpu(drv_data->humidity.val) *
179 				(1000000 / 512);
180 		val->val1 = humidity_frac / 1000000;
181 		val->val2 = humidity_frac % 1000000;
182 
183 		break;
184 	default:
185 		return -ENOTSUP;
186 	}
187 
188 	return 0;
189 }
190 
ens210_sys_reset(const struct device * dev)191 static int ens210_sys_reset(const struct device *dev)
192 {
193 	const struct ens210_config *config = dev->config;
194 
195 	const struct ens210_sys_ctrl sys_ctrl = {
196 			.low_power = 0,
197 			.reset = 1
198 	};
199 	int ret;
200 
201 	ret = i2c_reg_write_byte_dt(&config->i2c, ENS210_REG_SYS_CTRL, *(uint8_t *)&sys_ctrl);
202 	if (ret < 0) {
203 		LOG_ERR("Failed to set SYS_CTRL to 0x%x", *(uint8_t *)&sys_ctrl);
204 	}
205 	return ret;
206 }
207 
ens210_sys_enable(const struct device * dev,uint8_t low_power)208 static int ens210_sys_enable(const struct device *dev, uint8_t low_power)
209 {
210 	const struct ens210_config *config = dev->config;
211 
212 	const struct ens210_sys_ctrl sys_ctrl = {
213 			.low_power = low_power,
214 			.reset = 0
215 	};
216 	int ret;
217 
218 	ret = i2c_reg_write_byte_dt(&config->i2c, ENS210_REG_SYS_CTRL, *(uint8_t *)&sys_ctrl);
219 	if (ret < 0) {
220 		LOG_ERR("Failed to set SYS_CTRL to 0x%x", *(uint8_t *)&sys_ctrl);
221 	}
222 	return ret;
223 }
224 
ens210_wait_boot(const struct device * dev)225 static int ens210_wait_boot(const struct device *dev)
226 {
227 	const struct ens210_config *config = dev->config;
228 
229 	int cnt;
230 	int ret;
231 	struct ens210_sys_stat sys_stat;
232 
233 	for (cnt = 0; cnt <= CONFIG_ENS210_MAX_STAT_RETRIES; cnt++) {
234 		ret = i2c_reg_read_byte_dt(&config->i2c, ENS210_REG_SYS_STAT, (uint8_t *)&sys_stat);
235 
236 		if (ret < 0) {
237 			k_sleep(K_MSEC(1));
238 			continue;
239 		}
240 
241 		if (sys_stat.sys_active) {
242 			return 0;
243 		}
244 
245 		if (cnt == 0) {
246 			ens210_sys_reset(dev);
247 		}
248 
249 		ens210_sys_enable(dev, 0);
250 
251 		k_sleep(K_MSEC(2));
252 	}
253 
254 	if (ret < 0) {
255 		LOG_ERR("Failed to read SYS_STATE");
256 	}
257 
258 
259 	LOG_ERR("Sensor is not in active state");
260 	return -EIO;
261 }
262 
263 static const struct sensor_driver_api en210_driver_api = {
264 	.sample_fetch = ens210_sample_fetch,
265 	.channel_get = ens210_channel_get,
266 };
267 
ens210_init(const struct device * dev)268 static int ens210_init(const struct device *dev)
269 {
270 	const struct ens210_config *config = dev->config;
271 	const struct ens210_sens_run sense_run = {
272 		.t_run = ENS210_T_RUN,
273 		.h_run = ENS210_H_RUN
274 	};
275 
276 #if defined(CONFIG_ENS210_TEMPERATURE_CONTINUOUS) \
277 	|| defined(CONFIG_ENS210_HUMIDITY_CONTINUOUS)
278 	const struct ens210_sens_start sense_start = {
279 		.t_start = ENS210_T_RUN,
280 		.h_start = ENS210_H_RUN
281 	};
282 #endif
283 
284 	int ret;
285 	uint16_t part_id;
286 
287 	if (!device_is_ready(config->i2c.bus)) {
288 		LOG_ERR("I2C bus device not ready");
289 		return -ENODEV;
290 	}
291 
292 	/* Wait until the device is ready. */
293 	ret = ens210_wait_boot(dev);
294 	if (ret < 0) {
295 		return -EIO;
296 	}
297 
298 	/* Check Hardware ID. This is only possible after device is ready
299 	 * and active
300 	 */
301 	ret = i2c_burst_read_dt(&config->i2c, ENS210_REG_PART_ID, (uint8_t *)&part_id,
302 				sizeof(part_id));
303 	if (ret < 0) {
304 		LOG_ERR("Failed to read Part ID register");
305 		return -EIO;
306 	}
307 
308 	if (part_id != ENS210_PART_ID) {
309 		LOG_ERR("Part ID does not match. Want 0x%x, got 0x%x",
310 			    ENS210_PART_ID, part_id);
311 		return -EIO;
312 	}
313 
314 	/* Enable low power mode */
315 	if ((ENS210_T_RUN | ENS210_H_RUN) == 0) {
316 		ens210_sys_enable(dev, 1);
317 	}
318 
319 	/* Set measurement mode*/
320 	ret = i2c_reg_write_byte_dt(&config->i2c, ENS210_REG_SENS_RUN, *(uint8_t *)&sense_run);
321 	if (ret < 0) {
322 		LOG_ERR("Failed to set SENS_RUN to 0x%x",
323 			    *(uint8_t *)&sense_run);
324 		return -EIO;
325 	}
326 
327 #if defined(CONFIG_ENS210_TEMPERATURE_CONTINUOUS) \
328 	|| defined(CONFIG_ENS210_HUMIDITY_CONTINUOUS)
329 	/* Start measuring */
330 	ret = i2c_reg_write_byte_dt(&config->i2c, ENS210_REG_SENS_START, *(uint8_t *)&sense_start);
331 	if (ret < 0) {
332 		LOG_ERR("Failed to set SENS_START to 0x%x",
333 			    *(uint8_t *)&sense_start);
334 		return -EIO;
335 	}
336 #endif
337 	return 0;
338 }
339 
340 #define ENS210_DEFINE(inst)								\
341 	static struct ens210_data ens210_data_##inst;					\
342 											\
343 	static const struct ens210_config ens210_config_##inst = {			\
344 		.i2c = I2C_DT_SPEC_INST_GET(inst),					\
345 	};										\
346 											\
347 	SENSOR_DEVICE_DT_INST_DEFINE(inst, ens210_init, NULL,				\
348 			      &ens210_data_##inst, &ens210_config_##inst, POST_KERNEL,	\
349 			      CONFIG_SENSOR_INIT_PRIORITY, &en210_driver_api);		\
350 
351 DT_INST_FOREACH_STATUS_OKAY(ENS210_DEFINE)
352