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