1 /*
2  * Copyright (c) 2018 Peter Bigot Consulting, LLC
3  * Copyright (c) 2018 Linaro Ltd.
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define DT_DRV_COMPAT ams_ccs811
9 
10 #include <zephyr/device.h>
11 #include <zephyr/drivers/gpio.h>
12 #include <zephyr/drivers/i2c.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/sys/byteorder.h>
15 #include <zephyr/sys/util.h>
16 #include <zephyr/drivers/sensor.h>
17 #include <zephyr/sys/__assert.h>
18 #include <zephyr/logging/log.h>
19 
20 #include "ccs811.h"
21 
22 LOG_MODULE_REGISTER(CCS811, CONFIG_SENSOR_LOG_LEVEL);
23 
set_wake(const struct device * dev,bool enable)24 static void set_wake(const struct device *dev, bool enable)
25 {
26 	const struct ccs811_config *config = dev->config;
27 
28 	gpio_pin_set_dt(&config->wake_gpio, enable);
29 	if (enable) {
30 		k_busy_wait(50);        /* t_WAKE = 50 us */
31 	} else {
32 		k_busy_wait(20);        /* t_DWAKE = 20 us */
33 	}
34 }
35 
36 /* Get STATUS register in low 8 bits, and if ERROR is set put ERROR_ID
37  * in bits 8..15.  These registers are available in both boot and
38  * application mode.
39  */
fetch_status(const struct device * dev)40 static int fetch_status(const struct device *dev)
41 {
42 	const struct ccs811_config *config = dev->config;
43 	uint8_t status;
44 	int rv;
45 
46 	if (i2c_reg_read_byte_dt(&config->i2c, CCS811_REG_STATUS, &status) < 0) {
47 		LOG_ERR("Failed to read Status register");
48 		return -EIO;
49 	}
50 
51 	rv = status;
52 	if (status & CCS811_STATUS_ERROR) {
53 		uint8_t error_id;
54 
55 		if (i2c_reg_read_byte_dt(&config->i2c, CCS811_REG_ERROR_ID, &error_id) < 0) {
56 			LOG_ERR("Failed to read ERROR_ID register");
57 			return -EIO;
58 		}
59 
60 		rv |= (error_id << 8);
61 	}
62 
63 	return rv;
64 }
65 
error_from_status(int status)66 static inline uint8_t error_from_status(int status)
67 {
68 	return status >> 8;
69 }
70 
ccs811_result(const struct device * dev)71 const struct ccs811_result_type *ccs811_result(const struct device *dev)
72 {
73 	struct ccs811_data *drv_data = dev->data;
74 
75 	return &drv_data->result;
76 }
77 
ccs811_configver_fetch(const struct device * dev,struct ccs811_configver_type * ptr)78 int ccs811_configver_fetch(const struct device *dev,
79 			   struct ccs811_configver_type *ptr)
80 {
81 	struct ccs811_data *drv_data = dev->data;
82 	const struct ccs811_config *config = dev->config;
83 	uint8_t cmd;
84 	int rc;
85 
86 	if (!ptr) {
87 		return -EINVAL;
88 	}
89 
90 	set_wake(dev, true);
91 	cmd = CCS811_REG_HW_VERSION;
92 	rc = i2c_write_read_dt(&config->i2c, &cmd, sizeof(cmd), &ptr->hw_version,
93 			       sizeof(ptr->hw_version));
94 	if (rc == 0) {
95 		cmd = CCS811_REG_FW_BOOT_VERSION;
96 		rc = i2c_write_read_dt(&config->i2c, &cmd, sizeof(cmd),
97 				       (uint8_t *)&ptr->fw_boot_version,
98 				       sizeof(ptr->fw_boot_version));
99 		ptr->fw_boot_version = sys_be16_to_cpu(ptr->fw_boot_version);
100 	}
101 
102 	if (rc == 0) {
103 		cmd = CCS811_REG_FW_APP_VERSION;
104 		rc = i2c_write_read_dt(&config->i2c, &cmd, sizeof(cmd),
105 				       (uint8_t *)&ptr->fw_app_version,
106 				       sizeof(ptr->fw_app_version));
107 		ptr->fw_app_version = sys_be16_to_cpu(ptr->fw_app_version);
108 	}
109 	if (rc == 0) {
110 		LOG_INF("HW %x FW %x APP %x",
111 			ptr->hw_version, ptr->fw_boot_version,
112 			ptr->fw_app_version);
113 	}
114 
115 	set_wake(dev, false);
116 	ptr->mode = drv_data->mode & CCS811_MODE_MSK;
117 
118 	return rc;
119 }
120 
ccs811_baseline_fetch(const struct device * dev)121 int ccs811_baseline_fetch(const struct device *dev)
122 {
123 	const uint8_t cmd = CCS811_REG_BASELINE;
124 	const struct ccs811_config *config = dev->config;
125 	int rc;
126 	uint16_t baseline;
127 
128 	set_wake(dev, true);
129 
130 	rc = i2c_write_read_dt(&config->i2c, &cmd, sizeof(cmd), (uint8_t *)&baseline,
131 			       sizeof(baseline));
132 	set_wake(dev, false);
133 	if (rc <= 0) {
134 		rc = baseline;
135 	}
136 
137 	return rc;
138 }
139 
ccs811_baseline_update(const struct device * dev,uint16_t baseline)140 int ccs811_baseline_update(const struct device *dev,
141 			   uint16_t baseline)
142 {
143 	const struct ccs811_config *config = dev->config;
144 	uint8_t buf[1 + sizeof(baseline)];
145 	int rc;
146 
147 	buf[0] = CCS811_REG_BASELINE;
148 	memcpy(buf + 1, &baseline, sizeof(baseline));
149 	set_wake(dev, true);
150 	rc = i2c_write_dt(&config->i2c, buf, sizeof(buf));
151 	set_wake(dev, false);
152 	return rc;
153 }
154 
ccs811_envdata_update(const struct device * dev,const struct sensor_value * temperature,const struct sensor_value * humidity)155 int ccs811_envdata_update(const struct device *dev,
156 			  const struct sensor_value *temperature,
157 			  const struct sensor_value *humidity)
158 {
159 	const struct ccs811_config *config = dev->config;
160 	int rc;
161 	uint8_t buf[5] = { CCS811_REG_ENV_DATA };
162 
163 	/*
164 	 * Environment data are represented in a broken whole/fraction
165 	 * system that specified a 9-bit fractional part to represent
166 	 * milli-units.  Since 1000 is greater than 512, the device
167 	 * actually only pays attention to the top bit, treating it as
168 	 * indicating 0.5.  So we only write the first octet (7-bit
169 	 * while plus 1-bit half).
170 	 *
171 	 * Humidity is simple: scale it by two and round to the
172 	 * nearest half.  Assume the fractional part is not
173 	 * negative.
174 	 */
175 	if (humidity) {
176 		int value = 2 * humidity->val1;
177 
178 		value += (250000 + humidity->val2) / 500000;
179 		if (value < 0) {
180 			value = 0;
181 		} else if (value > (2 * 100)) {
182 			value = 2 * 100;
183 		}
184 		LOG_DBG("HUM %d.%06d becomes %d",
185 			humidity->val1, humidity->val2, value);
186 		buf[1] = value;
187 	} else {
188 		buf[1] = 2 * 50;
189 	}
190 
191 	/*
192 	 * Temperature is offset from -25 Cel.  Values below minimum
193 	 * store as zero.  Default is 25 Cel.  Again we round to the
194 	 * nearest half, complicated by Zephyr's signed representation
195 	 * of the fractional part.
196 	 */
197 	if (temperature) {
198 		int value = 2 * temperature->val1;
199 
200 		if (temperature->val2 < 0) {
201 			value += (250000 + temperature->val2) / 500000;
202 		} else {
203 			value += (-250000 + temperature->val2) / 500000;
204 		}
205 		if (value < (2 * -25)) {
206 			value = 0;
207 		} else {
208 			value += 2 * 25;
209 		}
210 		LOG_DBG("TEMP %d.%06d becomes %d",
211 			temperature->val1, temperature->val2, value);
212 		buf[3] = value;
213 	} else {
214 		buf[3] = 2 * (25 + 25);
215 	}
216 
217 	set_wake(dev, true);
218 	rc = i2c_write_dt(&config->i2c, buf, sizeof(buf));
219 	set_wake(dev, false);
220 	return rc;
221 }
222 
ccs811_sample_fetch(const struct device * dev,enum sensor_channel chan)223 static int ccs811_sample_fetch(const struct device *dev,
224 			       enum sensor_channel chan)
225 {
226 	struct ccs811_data *drv_data = dev->data;
227 	const struct ccs811_config *config = dev->config;
228 	struct ccs811_result_type *rp = &drv_data->result;
229 	const uint8_t cmd = CCS811_REG_ALG_RESULT_DATA;
230 	int rc;
231 	uint16_t buf[4] = { 0 };
232 	unsigned int status;
233 
234 	set_wake(dev, true);
235 	rc = i2c_write_read_dt(&config->i2c, &cmd, sizeof(cmd), (uint8_t *)buf, sizeof(buf));
236 	set_wake(dev, false);
237 	if (rc < 0) {
238 		return -EIO;
239 	}
240 
241 	rp->co2 = sys_be16_to_cpu(buf[0]);
242 	rp->voc = sys_be16_to_cpu(buf[1]);
243 	status = sys_le16_to_cpu(buf[2]); /* sic */
244 	rp->status = status;
245 	rp->error = error_from_status(status);
246 	rp->raw = sys_be16_to_cpu(buf[3]);
247 
248 	/* APP FW 1.1 does not set DATA_READY, but it does set CO2 to
249 	 * zero while it's starting up.  Assume a non-zero CO2 with
250 	 * old firmware is valid for the purposes of claiming the
251 	 * fetch was fresh.
252 	 */
253 	if ((drv_data->app_fw_ver <= 0x11)
254 	    && (rp->co2 != 0)) {
255 		status |= CCS811_STATUS_DATA_READY;
256 	}
257 	return (status & CCS811_STATUS_DATA_READY) ? 0 : -EAGAIN;
258 }
259 
ccs811_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)260 static int ccs811_channel_get(const struct device *dev,
261 			      enum sensor_channel chan,
262 			      struct sensor_value *val)
263 {
264 	struct ccs811_data *drv_data = dev->data;
265 	const struct ccs811_result_type *rp = &drv_data->result;
266 	uint32_t uval;
267 
268 	switch (chan) {
269 	case SENSOR_CHAN_CO2:
270 		val->val1 = rp->co2;
271 		val->val2 = 0;
272 
273 		break;
274 	case SENSOR_CHAN_VOC:
275 		val->val1 = rp->voc;
276 		val->val2 = 0;
277 
278 		break;
279 	case SENSOR_CHAN_VOLTAGE:
280 		/*
281 		 * Raw ADC readings are contained in least significant 10 bits
282 		 */
283 		uval = ((rp->raw & CCS811_RAW_VOLTAGE_MSK)
284 			>> CCS811_RAW_VOLTAGE_POS) * CCS811_RAW_VOLTAGE_SCALE;
285 		val->val1 = uval / 1000000U;
286 		val->val2 = uval % 1000000;
287 
288 		break;
289 	case SENSOR_CHAN_CURRENT:
290 		/*
291 		 * Current readings are contained in most
292 		 * significant 6 bits in microAmps
293 		 */
294 		uval = ((rp->raw & CCS811_RAW_CURRENT_MSK)
295 			>> CCS811_RAW_CURRENT_POS) * CCS811_RAW_CURRENT_SCALE;
296 		val->val1 = uval / 1000000U;
297 		val->val2 = uval % 1000000;
298 
299 		break;
300 	default:
301 		return -ENOTSUP;
302 	}
303 
304 	return 0;
305 }
306 
307 static const struct sensor_driver_api ccs811_driver_api = {
308 #ifdef CONFIG_CCS811_TRIGGER
309 	.attr_set = ccs811_attr_set,
310 	.trigger_set = ccs811_trigger_set,
311 #endif
312 	.sample_fetch = ccs811_sample_fetch,
313 	.channel_get = ccs811_channel_get,
314 };
315 
switch_to_app_mode(const struct device * dev)316 static int switch_to_app_mode(const struct device *dev)
317 {
318 	const struct ccs811_config *config = dev->config;
319 	uint8_t buf;
320 	int status;
321 
322 	LOG_DBG("Switching to Application mode...");
323 
324 	status = fetch_status(dev);
325 	if (status < 0) {
326 		return -EIO;
327 	}
328 
329 	/* Check for the application firmware */
330 	if (!(status & CCS811_STATUS_APP_VALID)) {
331 		LOG_ERR("No Application firmware loaded");
332 		return -EINVAL;
333 	}
334 
335 	/* Check if already in application mode */
336 	if (status & CCS811_STATUS_FW_MODE) {
337 		LOG_DBG("CCS811 Already in application mode");
338 		return 0;
339 	}
340 
341 	buf = CCS811_REG_APP_START;
342 	/* Set the device to application mode */
343 	if (i2c_write_dt(&config->i2c, &buf, 1) < 0) {
344 		LOG_ERR("Failed to set Application mode");
345 		return -EIO;
346 	}
347 
348 	k_msleep(1);             /* t_APP_START */
349 	status = fetch_status(dev);
350 	if (status < 0) {
351 		return -EIO;
352 	}
353 
354 	/* Check for application mode */
355 	if (!(status & CCS811_STATUS_FW_MODE)) {
356 		LOG_ERR("Failed to start Application firmware");
357 		return -EINVAL;
358 	}
359 
360 	LOG_DBG("CCS811 Application firmware started!");
361 
362 	return 0;
363 }
364 
365 #ifdef CONFIG_CCS811_TRIGGER
366 
ccs811_mutate_meas_mode(const struct device * dev,uint8_t set,uint8_t clear)367 int ccs811_mutate_meas_mode(const struct device *dev,
368 			    uint8_t set,
369 			    uint8_t clear)
370 {
371 	struct ccs811_data *drv_data = dev->data;
372 	const struct ccs811_config *config = dev->config;
373 	int rc = 0;
374 	uint8_t mode = set | (drv_data->mode & ~clear);
375 
376 	/*
377 	 * Changing drive mode of a running system has preconditions.
378 	 * Only allow changing the interrupt generation.
379 	 */
380 	if ((set | clear) & ~(CCS811_MODE_DATARDY | CCS811_MODE_THRESH)) {
381 		return -EINVAL;
382 	}
383 
384 	if (mode != drv_data->mode) {
385 		set_wake(dev, true);
386 		rc = i2c_reg_write_byte_dt(&config->i2c, CCS811_REG_MEAS_MODE, mode);
387 		LOG_DBG("CCS811 meas mode change %02x to %02x got %d",
388 			drv_data->mode, mode, rc);
389 		if (rc < 0) {
390 			LOG_ERR("Failed to set mode");
391 			rc = -EIO;
392 		} else {
393 			drv_data->mode = mode;
394 			rc = 0;
395 		}
396 
397 		set_wake(dev, false);
398 	}
399 
400 	return rc;
401 }
402 
ccs811_set_thresholds(const struct device * dev)403 int ccs811_set_thresholds(const struct device *dev)
404 {
405 	struct ccs811_data *drv_data = dev->data;
406 	const struct ccs811_config *config = dev->config;
407 	const uint8_t buf[5] = {
408 		CCS811_REG_THRESHOLDS,
409 		drv_data->co2_l2m >> 8,
410 		drv_data->co2_l2m,
411 		drv_data->co2_m2h >> 8,
412 		drv_data->co2_m2h,
413 	};
414 	int rc;
415 
416 	set_wake(dev, true);
417 	rc = i2c_write_dt(&config->i2c, buf, sizeof(buf));
418 	set_wake(dev, false);
419 	return rc;
420 }
421 
422 #endif /* CONFIG_CCS811_TRIGGER */
423 
ccs811_init(const struct device * dev)424 static int ccs811_init(const struct device *dev)
425 {
426 	struct ccs811_data *drv_data = dev->data;
427 	const struct ccs811_config *config = dev->config;
428 	int ret = 0;
429 	int status;
430 	uint16_t fw_ver;
431 	uint8_t cmd;
432 	uint8_t hw_id;
433 
434 	if (!device_is_ready(config->i2c.bus)) {
435 		LOG_ERR("I2C bus device not ready");
436 		return -ENODEV;
437 	}
438 
439 	if (config->wake_gpio.port) {
440 		if (!gpio_is_ready_dt(&config->wake_gpio)) {
441 			LOG_ERR("GPIO device not ready");
442 			return -ENODEV;
443 		}
444 
445 		/*
446 		 * Wakeup pin should be pulled low before initiating
447 		 * any I2C transfer.  If it has been tied to GND by
448 		 * default, skip this part.
449 		 */
450 		gpio_pin_configure_dt(&config->wake_gpio, GPIO_OUTPUT_INACTIVE);
451 
452 		set_wake(dev, true);
453 		k_msleep(1);
454 	}
455 
456 	if (config->reset_gpio.port) {
457 		if (!gpio_is_ready_dt(&config->reset_gpio)) {
458 			LOG_ERR("GPIO device not ready");
459 			return -ENODEV;
460 		}
461 
462 		gpio_pin_configure_dt(&config->reset_gpio, GPIO_OUTPUT_ACTIVE);
463 
464 		k_msleep(1);
465 	}
466 
467 	if (config->irq_gpio.port) {
468 		if (!gpio_is_ready_dt(&config->irq_gpio)) {
469 			LOG_ERR("GPIO device not ready");
470 			return -ENODEV;
471 		}
472 	}
473 
474 	k_msleep(20);            /* t_START assuming recent power-on */
475 
476 	/* Reset the device.  This saves having to deal with detecting
477 	 * and validating any errors or configuration inconsistencies
478 	 * after a reset that left the device running.
479 	 */
480 	if (config->reset_gpio.port) {
481 		gpio_pin_set_dt(&config->reset_gpio, 1);
482 		k_busy_wait(15);        /* t_RESET */
483 		gpio_pin_set_dt(&config->reset_gpio, 0);
484 	} else {
485 		static uint8_t const reset_seq[] = {
486 			0xFF, 0x11, 0xE5, 0x72, 0x8A,
487 		};
488 
489 		if (i2c_write_dt(&config->i2c, reset_seq, sizeof(reset_seq)) < 0) {
490 			LOG_ERR("Failed to issue SW reset");
491 			ret = -EIO;
492 			goto out;
493 		}
494 	}
495 
496 	k_msleep(2);             /* t_START after reset */
497 
498 	/* Switch device to application mode */
499 	ret = switch_to_app_mode(dev);
500 	if (ret) {
501 		goto out;
502 	}
503 
504 	/* Check Hardware ID */
505 	if (i2c_reg_read_byte_dt(&config->i2c, CCS811_REG_HW_ID, &hw_id) < 0) {
506 		LOG_ERR("Failed to read Hardware ID register");
507 		ret = -EIO;
508 		goto out;
509 	}
510 
511 	if (hw_id != CCS881_HW_ID) {
512 		LOG_ERR("Hardware ID mismatch!");
513 		ret = -EINVAL;
514 		goto out;
515 	}
516 
517 	/* Check application firmware version (first byte) */
518 	cmd = CCS811_REG_FW_APP_VERSION;
519 	if (i2c_write_read_dt(&config->i2c, &cmd, sizeof(cmd), &fw_ver, sizeof(fw_ver)) < 0) {
520 		LOG_ERR("Failed to read App Firmware Version register");
521 		ret = -EIO;
522 		goto out;
523 	}
524 	fw_ver = sys_be16_to_cpu(fw_ver);
525 	LOG_INF("App FW %04x", fw_ver);
526 	drv_data->app_fw_ver = fw_ver >> 8U;
527 
528 	/* Configure measurement mode */
529 	uint8_t meas_mode = CCS811_MODE_IDLE;
530 #ifdef CONFIG_CCS811_DRIVE_MODE_1
531 	meas_mode = CCS811_MODE_IAQ_1SEC;
532 #elif defined(CONFIG_CCS811_DRIVE_MODE_2)
533 	meas_mode = CCS811_MODE_IAQ_10SEC;
534 #elif defined(CONFIG_CCS811_DRIVE_MODE_3)
535 	meas_mode = CCS811_MODE_IAQ_60SEC;
536 #elif defined(CONFIG_CCS811_DRIVE_MODE_4)
537 	meas_mode = CCS811_MODE_IAQ_250MSEC;
538 #endif
539 	if (i2c_reg_write_byte_dt(&config->i2c, CCS811_REG_MEAS_MODE, meas_mode) < 0) {
540 		LOG_ERR("Failed to set Measurement mode");
541 		ret = -EIO;
542 		goto out;
543 	}
544 	drv_data->mode = meas_mode;
545 
546 	/* Check for error */
547 	status = fetch_status(dev);
548 	if (status < 0) {
549 		ret = -EIO;
550 		goto out;
551 	}
552 
553 	if (status & CCS811_STATUS_ERROR) {
554 		LOG_ERR("CCS811 Error %02x during sensor configuration",
555 			error_from_status(status));
556 		ret = -EINVAL;
557 		goto out;
558 	}
559 
560 #ifdef CONFIG_CCS811_TRIGGER
561 	if (config->irq_gpio.port) {
562 		ret = ccs811_init_interrupt(dev);
563 		LOG_DBG("CCS811 interrupt init got %d", ret);
564 	}
565 #endif
566 
567 out:
568 	set_wake(dev, false);
569 	return ret;
570 }
571 
572 #define CCS811_DEFINE(inst)									\
573 	static struct ccs811_data ccs811_data_##inst;						\
574 												\
575 	static const struct ccs811_config ccs811_config_##inst = {				\
576 		.i2c = I2C_DT_SPEC_INST_GET(inst),						\
577 		IF_ENABLED(CONFIG_CCS811_TRIGGER,						\
578 			   (.irq_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, irq_gpios, { 0 }),))	\
579 		.reset_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, reset_gpios, { 0 }),		\
580 		.wake_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, wake_gpios, { 0 }),			\
581 	};											\
582 												\
583 	SENSOR_DEVICE_DT_INST_DEFINE(0, ccs811_init, NULL,					\
584 			      &ccs811_data_##inst, &ccs811_config_##inst,			\
585 			      POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,				\
586 			      &ccs811_driver_api);						\
587 
588 DT_INST_FOREACH_STATUS_OKAY(CCS811_DEFINE)
589