2018-01-05 08:09:05 -08:00
|
|
|
/*
|
2018-11-19 22:21:15 -06:00
|
|
|
* Copyright (c) 2018 Peter Bigot Consulting, LLC
|
2018-01-05 08:09:05 -08:00
|
|
|
* Copyright (c) 2018 Linaro Ltd.
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <device.h>
|
2019-06-25 15:53:52 -04:00
|
|
|
#include <drivers/gpio.h>
|
2019-06-25 15:53:54 -04:00
|
|
|
#include <drivers/i2c.h>
|
2018-01-05 08:09:05 -08:00
|
|
|
#include <kernel.h>
|
2019-06-26 10:33:41 -04:00
|
|
|
#include <sys/byteorder.h>
|
2019-06-26 10:33:55 -04:00
|
|
|
#include <sys/util.h>
|
2019-06-25 15:54:00 -04:00
|
|
|
#include <drivers/sensor.h>
|
2019-06-26 10:33:39 -04:00
|
|
|
#include <sys/__assert.h>
|
2018-10-10 12:08:54 +05:30
|
|
|
#include <logging/log.h>
|
2018-01-05 08:09:05 -08:00
|
|
|
|
|
|
|
#include "ccs811.h"
|
|
|
|
|
2019-10-11 12:40:57 +02:00
|
|
|
LOG_MODULE_REGISTER(CCS811, CONFIG_SENSOR_LOG_LEVEL);
|
2018-10-10 12:08:54 +05:30
|
|
|
|
2018-11-19 23:02:30 -06:00
|
|
|
#ifdef DT_INST_0_AMS_CCS811_WAKE_GPIOS_CONTROLLER
|
|
|
|
static void set_wake(struct ccs811_data *drv_data, bool enable)
|
|
|
|
{
|
|
|
|
/* Always active-low */
|
2018-11-21 14:35:01 -06:00
|
|
|
gpio_pin_write(drv_data->wake_gpio, DT_INST_0_AMS_CCS811_WAKE_GPIOS_PIN, !enable);
|
2018-11-19 23:02:30 -06:00
|
|
|
if (enable) {
|
|
|
|
k_busy_wait(50); /* t_WAKE = 50 us */
|
|
|
|
} else {
|
|
|
|
k_busy_wait(20); /* t_DWAKE = 20 us */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#define set_wake(...)
|
|
|
|
#endif
|
|
|
|
|
2018-11-20 10:31:19 -06:00
|
|
|
/* Get STATUS register in low 8 bits, and if ERROR is set put ERROR_ID
|
|
|
|
* in bits 8..15. These registers are available in both boot and
|
|
|
|
* application mode.
|
|
|
|
*/
|
|
|
|
static int fetch_status(struct device *i2c)
|
|
|
|
{
|
|
|
|
u8_t status;
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
if (i2c_reg_read_byte(i2c, DT_INST_0_AMS_CCS811_BASE_ADDRESS,
|
|
|
|
CCS811_REG_STATUS, &status) < 0) {
|
|
|
|
LOG_ERR("Failed to read Status register");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = status;
|
|
|
|
if (status & CCS811_STATUS_ERROR) {
|
|
|
|
u8_t error_id;
|
|
|
|
|
|
|
|
if (i2c_reg_read_byte(i2c, DT_INST_0_AMS_CCS811_BASE_ADDRESS,
|
|
|
|
CCS811_REG_ERROR_ID, &error_id) < 0) {
|
|
|
|
LOG_ERR("Failed to read ERROR_ID register");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv |= (error_id << 8);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline u8_t error_from_status(int status)
|
|
|
|
{
|
|
|
|
return status >> 8;
|
|
|
|
}
|
|
|
|
|
2018-11-22 16:10:48 -06:00
|
|
|
int ccs811_baseline_fetch(struct device *dev)
|
|
|
|
{
|
|
|
|
const u8_t cmd = CCS811_REG_BASELINE;
|
|
|
|
struct ccs811_data *drv_data = dev->driver_data;
|
|
|
|
int rc;
|
|
|
|
u16_t baseline;
|
|
|
|
|
|
|
|
set_wake(drv_data, true);
|
|
|
|
|
|
|
|
rc = i2c_write_read(drv_data->i2c, DT_INST_0_AMS_CCS811_BASE_ADDRESS,
|
|
|
|
&cmd, sizeof(cmd),
|
|
|
|
(u8_t *)&baseline, sizeof(baseline));
|
|
|
|
set_wake(drv_data, false);
|
|
|
|
if (rc <= 0) {
|
|
|
|
rc = baseline;
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ccs811_baseline_update(struct device *dev,
|
|
|
|
u16_t baseline)
|
|
|
|
{
|
|
|
|
struct ccs811_data *drv_data = dev->driver_data;
|
|
|
|
u8_t buf[1 + sizeof(baseline)];
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
buf[0] = CCS811_REG_BASELINE;
|
|
|
|
memcpy(buf + 1, &baseline, sizeof(baseline));
|
|
|
|
set_wake(drv_data, true);
|
|
|
|
rc = i2c_write(drv_data->i2c, buf, sizeof(buf), DT_INST_0_AMS_CCS811_BASE_ADDRESS);
|
|
|
|
set_wake(drv_data, false);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2018-01-05 08:09:05 -08:00
|
|
|
static int ccs811_sample_fetch(struct device *dev, enum sensor_channel chan)
|
|
|
|
{
|
|
|
|
struct ccs811_data *drv_data = dev->driver_data;
|
2018-11-21 13:16:13 -06:00
|
|
|
int rc;
|
2018-11-20 06:05:38 -06:00
|
|
|
int tries;
|
2018-01-05 08:09:05 -08:00
|
|
|
u16_t buf[4];
|
2018-11-20 10:31:19 -06:00
|
|
|
int status;
|
2018-01-05 08:09:05 -08:00
|
|
|
|
2018-11-20 06:05:38 -06:00
|
|
|
/* Check data ready flag for the measurement interval */
|
|
|
|
#ifdef CONFIG_CCS811_DRIVE_MODE_1
|
|
|
|
tries = 11;
|
|
|
|
#elif defined(CONFIG_CCS811_DRIVE_MODE_2)
|
|
|
|
tries = 101;
|
|
|
|
#elif defined(CONFIG_CCS811_DRIVE_MODE_3)
|
|
|
|
tries = 601;
|
|
|
|
#endif
|
|
|
|
|
2018-11-21 13:16:13 -06:00
|
|
|
do {
|
|
|
|
const u8_t cmd = CCS811_REG_ALG_RESULT_DATA;
|
|
|
|
|
2018-11-19 23:02:30 -06:00
|
|
|
set_wake(drv_data, true);
|
2018-11-21 13:16:13 -06:00
|
|
|
rc = i2c_write_read(drv_data->i2c, DT_INST_0_AMS_CCS811_BASE_ADDRESS,
|
|
|
|
&cmd, sizeof(cmd),
|
|
|
|
(u8_t *)buf, sizeof(buf));
|
|
|
|
|
|
|
|
set_wake(drv_data, false);
|
|
|
|
if (rc < 0) {
|
|
|
|
return -EIO;
|
2018-11-20 10:31:19 -06:00
|
|
|
}
|
|
|
|
|
2018-11-21 13:16:13 -06:00
|
|
|
status = buf[2];
|
2018-11-20 10:31:19 -06:00
|
|
|
if (status & CCS811_STATUS_ERROR) {
|
|
|
|
LOG_ERR("CCS811 ERROR ID %02x",
|
|
|
|
error_from_status(status));
|
2018-11-21 13:16:13 -06:00
|
|
|
return -EIO;
|
2018-01-05 08:09:05 -08:00
|
|
|
}
|
|
|
|
|
2018-11-21 13:16:13 -06:00
|
|
|
if (status & CCS811_STATUS_DATA_READY) {
|
2018-01-05 08:09:05 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-10-06 14:02:31 -05:00
|
|
|
k_sleep(K_MSEC(100));
|
2018-11-21 13:16:13 -06:00
|
|
|
} while (--tries > 0);
|
2018-01-05 08:09:05 -08:00
|
|
|
if (!(status & CCS811_STATUS_DATA_READY)) {
|
2018-11-21 13:16:13 -06:00
|
|
|
return -EIO;
|
2018-01-05 08:09:05 -08:00
|
|
|
}
|
|
|
|
drv_data->co2 = sys_be16_to_cpu(buf[0]);
|
|
|
|
drv_data->voc = sys_be16_to_cpu(buf[1]);
|
2018-11-21 13:16:13 -06:00
|
|
|
drv_data->status = status;
|
|
|
|
drv_data->error = error_from_status(status);
|
2018-01-05 08:09:05 -08:00
|
|
|
drv_data->resistance = sys_be16_to_cpu(buf[3]);
|
2018-11-21 13:16:13 -06:00
|
|
|
return 0;
|
2018-01-05 08:09:05 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ccs811_channel_get(struct device *dev,
|
2018-11-21 14:43:31 -06:00
|
|
|
enum sensor_channel chan,
|
|
|
|
struct sensor_value *val)
|
2018-01-05 08:09:05 -08:00
|
|
|
{
|
|
|
|
struct ccs811_data *drv_data = dev->driver_data;
|
|
|
|
u32_t uval;
|
|
|
|
|
|
|
|
switch (chan) {
|
|
|
|
case SENSOR_CHAN_CO2:
|
2018-02-24 16:25:56 +02:00
|
|
|
val->val1 = drv_data->co2;
|
|
|
|
val->val2 = 0;
|
2018-01-05 08:09:05 -08:00
|
|
|
|
|
|
|
break;
|
|
|
|
case SENSOR_CHAN_VOC:
|
2018-02-24 16:25:56 +02:00
|
|
|
val->val1 = drv_data->voc;
|
|
|
|
val->val2 = 0;
|
2018-01-05 08:09:05 -08:00
|
|
|
|
|
|
|
break;
|
|
|
|
case SENSOR_CHAN_VOLTAGE:
|
|
|
|
/*
|
2018-02-24 16:25:56 +02:00
|
|
|
* Raw ADC readings are contained in least significant 10 bits
|
2018-01-05 08:09:05 -08:00
|
|
|
*/
|
|
|
|
uval = (drv_data->resistance & CCS811_VOLTAGE_MASK)
|
2018-11-21 14:43:31 -06:00
|
|
|
* CCS811_VOLTAGE_SCALE;
|
2019-03-26 19:57:45 -06:00
|
|
|
val->val1 = uval / 1000000U;
|
2018-01-05 08:09:05 -08:00
|
|
|
val->val2 = uval % 1000000;
|
|
|
|
|
|
|
|
break;
|
|
|
|
case SENSOR_CHAN_CURRENT:
|
|
|
|
/*
|
|
|
|
* Current readings are contained in most
|
|
|
|
* significant 6 bits in microAmps
|
|
|
|
*/
|
|
|
|
uval = drv_data->resistance >> 10;
|
2019-03-26 19:57:45 -06:00
|
|
|
val->val1 = uval / 1000000U;
|
2018-01-05 08:09:05 -08:00
|
|
|
val->val2 = uval % 1000000;
|
|
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct sensor_driver_api ccs811_driver_api = {
|
2018-11-21 14:35:01 -06:00
|
|
|
#ifdef CONFIG_CCS811_TRIGGER
|
|
|
|
.attr_set = ccs811_attr_set,
|
|
|
|
.trigger_set = ccs811_trigger_set,
|
|
|
|
#endif
|
2018-01-05 08:09:05 -08:00
|
|
|
.sample_fetch = ccs811_sample_fetch,
|
|
|
|
.channel_get = ccs811_channel_get,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int switch_to_app_mode(struct device *i2c)
|
|
|
|
{
|
2018-11-20 10:31:19 -06:00
|
|
|
u8_t buf;
|
|
|
|
int status;
|
2018-01-05 08:09:05 -08:00
|
|
|
|
2018-10-10 12:08:54 +05:30
|
|
|
LOG_DBG("Switching to Application mode...");
|
2018-01-05 08:09:05 -08:00
|
|
|
|
2018-11-20 10:31:19 -06:00
|
|
|
status = fetch_status(i2c);
|
|
|
|
if (status < 0) {
|
2018-01-05 08:09:05 -08:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for the application firmware */
|
|
|
|
if (!(status & CCS811_STATUS_APP_VALID)) {
|
2018-10-10 12:08:54 +05:30
|
|
|
LOG_ERR("No Application firmware loaded");
|
2018-01-05 08:09:05 -08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-11-20 08:47:19 -06:00
|
|
|
/* Check if already in application mode */
|
|
|
|
if (status & CCS811_STATUS_FW_MODE) {
|
|
|
|
LOG_DBG("CCS811 Already in application mode");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-01-05 08:09:05 -08:00
|
|
|
buf = CCS811_REG_APP_START;
|
|
|
|
/* Set the device to application mode */
|
2019-06-11 14:20:32 -05:00
|
|
|
if (i2c_write(i2c, &buf, 1, DT_INST_0_AMS_CCS811_BASE_ADDRESS) < 0) {
|
2018-10-10 12:08:54 +05:30
|
|
|
LOG_ERR("Failed to set Application mode");
|
2018-01-05 08:09:05 -08:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2018-11-20 08:47:19 -06:00
|
|
|
k_sleep(1); /* t_APP_START */
|
2018-11-20 10:31:19 -06:00
|
|
|
status = fetch_status(i2c);
|
|
|
|
if (status < 0) {
|
2018-01-05 08:09:05 -08:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for application mode */
|
|
|
|
if (!(status & CCS811_STATUS_FW_MODE)) {
|
2018-10-10 12:08:54 +05:30
|
|
|
LOG_ERR("Failed to start Application firmware");
|
2018-01-05 08:09:05 -08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-10-10 12:08:54 +05:30
|
|
|
LOG_DBG("CCS811 Application firmware started!");
|
2018-01-05 08:09:05 -08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-11-21 14:35:01 -06:00
|
|
|
#ifdef CONFIG_CCS811_TRIGGER
|
|
|
|
|
|
|
|
int ccs811_mutate_meas_mode(struct device *dev,
|
|
|
|
u8_t set,
|
|
|
|
u8_t clear)
|
|
|
|
{
|
|
|
|
struct ccs811_data *drv_data = dev->driver_data;
|
|
|
|
int rc = 0;
|
|
|
|
u8_t mode = set | (drv_data->mode & ~clear);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Changing drive mode of a running system has preconditions.
|
|
|
|
* Only allow changing the interrupt generation.
|
|
|
|
*/
|
|
|
|
if ((set | clear) & ~(CCS811_MODE_DATARDY | CCS811_MODE_THRESH)) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mode != drv_data->mode) {
|
|
|
|
set_wake(drv_data, true);
|
|
|
|
rc = i2c_reg_write_byte(drv_data->i2c, DT_INST_0_AMS_CCS811_BASE_ADDRESS,
|
|
|
|
CCS811_REG_MEAS_MODE,
|
|
|
|
mode);
|
|
|
|
LOG_DBG("CCS811 meas mode change %02x to %02x got %d",
|
|
|
|
drv_data->mode, mode, rc);
|
|
|
|
if (rc < 0) {
|
|
|
|
LOG_ERR("Failed to set mode");
|
|
|
|
rc = -EIO;
|
|
|
|
} else {
|
|
|
|
drv_data->mode = mode;
|
|
|
|
rc = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
set_wake(drv_data, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ccs811_set_thresholds(struct device *dev)
|
|
|
|
{
|
|
|
|
struct ccs811_data *drv_data = dev->driver_data;
|
|
|
|
const u8_t buf[5] = {
|
|
|
|
CCS811_REG_THRESHOLDS,
|
|
|
|
drv_data->co2_l2m >> 8,
|
|
|
|
drv_data->co2_l2m,
|
|
|
|
drv_data->co2_m2h >> 8,
|
|
|
|
drv_data->co2_m2h,
|
|
|
|
};
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
set_wake(drv_data, true);
|
|
|
|
rc = i2c_write(drv_data->i2c, buf, sizeof(buf), DT_INST_0_AMS_CCS811_BASE_ADDRESS);
|
|
|
|
set_wake(drv_data, false);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* CONFIG_CCS811_TRIGGER */
|
|
|
|
|
2018-11-22 16:10:48 -06:00
|
|
|
static int ccs811_init(struct device *dev)
|
2018-01-05 08:09:05 -08:00
|
|
|
{
|
|
|
|
struct ccs811_data *drv_data = dev->driver_data;
|
2018-11-20 10:31:19 -06:00
|
|
|
int ret = 0;
|
|
|
|
u8_t hw_id;
|
|
|
|
int status;
|
2018-01-05 08:09:05 -08:00
|
|
|
|
2018-11-21 14:35:01 -06:00
|
|
|
*drv_data = (struct ccs811_data){ 0 };
|
2019-06-11 14:20:32 -05:00
|
|
|
drv_data->i2c = device_get_binding(DT_INST_0_AMS_CCS811_BUS_NAME);
|
2018-01-05 08:09:05 -08:00
|
|
|
if (drv_data->i2c == NULL) {
|
2018-10-10 12:08:54 +05:30
|
|
|
LOG_ERR("Failed to get pointer to %s device!",
|
2018-11-21 14:43:31 -06:00
|
|
|
DT_INST_0_AMS_CCS811_BUS_NAME);
|
2018-01-05 08:09:05 -08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2019-11-05 08:04:34 -06:00
|
|
|
#ifdef DT_INST_0_AMS_CCS811_WAKE_GPIOS_CONTROLLER
|
2018-11-21 14:35:01 -06:00
|
|
|
drv_data->wake_gpio = device_get_binding(DT_INST_0_AMS_CCS811_WAKE_GPIOS_CONTROLLER);
|
|
|
|
if (drv_data->wake_gpio == NULL) {
|
2018-11-19 22:21:15 -06:00
|
|
|
LOG_ERR("Failed to get pointer to WAKE device: %s",
|
2019-11-05 08:04:34 -06:00
|
|
|
DT_INST_0_AMS_CCS811_WAKE_GPIOS_CONTROLLER);
|
2018-01-05 08:09:05 -08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2018-03-15 18:16:38 +02:00
|
|
|
|
2018-11-21 14:35:01 -06:00
|
|
|
/*
|
|
|
|
* Wakeup pin should be pulled low before initiating
|
|
|
|
* any I2C transfer. If it has been tied to GND by
|
|
|
|
* default, skip this part.
|
|
|
|
*/
|
|
|
|
gpio_pin_configure(drv_data->wake_gpio, DT_INST_0_AMS_CCS811_WAKE_GPIOS_PIN,
|
|
|
|
GPIO_DIR_OUT);
|
|
|
|
|
|
|
|
set_wake(drv_data, true);
|
|
|
|
k_sleep(1);
|
2018-11-19 22:21:15 -06:00
|
|
|
#endif
|
2019-11-05 08:04:34 -06:00
|
|
|
#ifdef DT_INST_0_AMS_CCS811_RESET_GPIOS_CONTROLLER
|
2018-11-21 14:35:01 -06:00
|
|
|
drv_data->reset_gpio = device_get_binding(DT_INST_0_AMS_CCS811_RESET_GPIOS_CONTROLLER);
|
|
|
|
if (drv_data->reset_gpio == NULL) {
|
2018-11-19 22:21:15 -06:00
|
|
|
LOG_ERR("Failed to get pointer to RESET device: %s",
|
2019-11-05 08:04:34 -06:00
|
|
|
DT_INST_0_AMS_CCS811_RESET_GPIOS_CONTROLLER);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2018-11-21 14:35:01 -06:00
|
|
|
gpio_pin_configure(drv_data->reset_gpio, DT_INST_0_AMS_CCS811_RESET_GPIOS_PIN,
|
|
|
|
GPIO_DIR_OUT);
|
|
|
|
gpio_pin_write(drv_data->reset_gpio, DT_INST_0_AMS_CCS811_RESET_GPIOS_PIN, 1);
|
2018-11-19 22:21:15 -06:00
|
|
|
|
2018-11-21 14:35:01 -06:00
|
|
|
k_sleep(1);
|
2019-11-05 08:04:34 -06:00
|
|
|
#endif
|
2018-01-05 08:09:05 -08:00
|
|
|
|
2018-11-21 14:35:01 -06:00
|
|
|
#ifdef DT_INST_0_AMS_CCS811_IRQ_GPIOS_CONTROLLER
|
|
|
|
drv_data->int_gpio = device_get_binding(DT_INST_0_AMS_CCS811_IRQ_GPIOS_CONTROLLER);
|
|
|
|
if (drv_data->int_gpio == NULL) {
|
|
|
|
LOG_ERR("Failed to get pointer to INT device: %s",
|
|
|
|
DT_INST_0_AMS_CCS811_IRQ_GPIOS_CONTROLLER);
|
|
|
|
return -EINVAL;
|
2018-11-19 22:21:15 -06:00
|
|
|
}
|
2018-11-21 14:35:01 -06:00
|
|
|
#endif
|
2018-01-05 08:09:05 -08:00
|
|
|
|
2018-11-21 08:37:52 -06:00
|
|
|
/* Reset the device. This saves having to deal with detecting
|
|
|
|
* and validating any errors or configuration inconsistencies
|
|
|
|
* after a reset that left the device running.
|
|
|
|
*/
|
|
|
|
#ifdef DT_INST_0_AMS_CCS811_RESET_GPIOS_PIN
|
2018-11-21 14:35:01 -06:00
|
|
|
gpio_pin_write(drv_data->reset_gpio, DT_INST_0_AMS_CCS811_RESET_GPIOS_PIN, 0);
|
|
|
|
k_busy_wait(15); /* t_RESET */
|
|
|
|
gpio_pin_write(drv_data->reset_gpio, DT_INST_0_AMS_CCS811_RESET_GPIOS_PIN, 1);
|
2018-11-21 08:37:52 -06:00
|
|
|
#else
|
|
|
|
{
|
|
|
|
static u8_t const reset_seq[] = {
|
|
|
|
0xFF, 0x11, 0xE5, 0x72, 0x8A,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (i2c_write(drv_data->i2c, reset_seq, sizeof(reset_seq),
|
|
|
|
DT_INST_0_AMS_CCS811_BASE_ADDRESS) < 0) {
|
|
|
|
LOG_ERR("Failed to issue SW reset");
|
|
|
|
ret = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
k_sleep(20); /* t_START assuming recent power-on */
|
|
|
|
|
2018-01-05 08:09:05 -08:00
|
|
|
/* Switch device to application mode */
|
|
|
|
ret = switch_to_app_mode(drv_data->i2c);
|
|
|
|
if (ret) {
|
2018-11-19 23:02:30 -06:00
|
|
|
goto out;
|
2018-01-05 08:09:05 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Check Hardware ID */
|
2019-06-11 14:20:32 -05:00
|
|
|
if (i2c_reg_read_byte(drv_data->i2c, DT_INST_0_AMS_CCS811_BASE_ADDRESS,
|
2018-01-05 08:09:05 -08:00
|
|
|
CCS811_REG_HW_ID, &hw_id) < 0) {
|
2018-10-10 12:08:54 +05:30
|
|
|
LOG_ERR("Failed to read Hardware ID register");
|
2018-11-19 23:02:30 -06:00
|
|
|
ret = -EIO;
|
|
|
|
goto out;
|
2018-01-05 08:09:05 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (hw_id != CCS881_HW_ID) {
|
2018-10-10 12:08:54 +05:30
|
|
|
LOG_ERR("Hardware ID mismatch!");
|
2018-11-19 23:02:30 -06:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
2018-01-05 08:09:05 -08:00
|
|
|
}
|
|
|
|
|
2018-11-20 06:05:38 -06:00
|
|
|
/* Configure measurement mode */
|
|
|
|
u8_t meas_mode = 0;
|
|
|
|
#ifdef CONFIG_CCS811_DRIVE_MODE_1
|
|
|
|
meas_mode = 0x10;
|
|
|
|
#elif defined(CONFIG_CCS811_DRIVE_MODE_2)
|
|
|
|
meas_mode = 0x20;
|
|
|
|
#elif defined(CONFIG_CCS811_DRIVE_MODE_3)
|
|
|
|
meas_mode = 0x30;
|
|
|
|
#endif
|
2019-06-11 14:20:32 -05:00
|
|
|
if (i2c_reg_write_byte(drv_data->i2c, DT_INST_0_AMS_CCS811_BASE_ADDRESS,
|
2018-01-05 08:09:05 -08:00
|
|
|
CCS811_REG_MEAS_MODE,
|
2018-11-20 06:05:38 -06:00
|
|
|
meas_mode) < 0) {
|
2018-10-10 12:08:54 +05:30
|
|
|
LOG_ERR("Failed to set Measurement mode");
|
2018-11-19 23:02:30 -06:00
|
|
|
ret = -EIO;
|
|
|
|
goto out;
|
2018-01-05 08:09:05 -08:00
|
|
|
}
|
2018-11-21 14:35:01 -06:00
|
|
|
drv_data->mode = meas_mode;
|
2018-01-05 08:09:05 -08:00
|
|
|
|
|
|
|
/* Check for error */
|
2018-11-20 10:31:19 -06:00
|
|
|
status = fetch_status(drv_data->i2c);
|
|
|
|
if (status < 0) {
|
2018-11-19 23:02:30 -06:00
|
|
|
ret = -EIO;
|
|
|
|
goto out;
|
2018-01-05 08:09:05 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (status & CCS811_STATUS_ERROR) {
|
2018-11-20 10:31:19 -06:00
|
|
|
LOG_ERR("CCS811 Error %02x during sensor configuration",
|
|
|
|
error_from_status(status));
|
2018-11-19 23:02:30 -06:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
2018-01-05 08:09:05 -08:00
|
|
|
}
|
|
|
|
|
2018-11-21 14:35:01 -06:00
|
|
|
#ifdef CONFIG_CCS811_TRIGGER
|
|
|
|
ret = ccs811_init_interrupt(dev);
|
|
|
|
LOG_DBG("CCS811 interrupt init got %d", ret);
|
|
|
|
#endif
|
|
|
|
|
2018-11-19 23:02:30 -06:00
|
|
|
out:
|
|
|
|
set_wake(drv_data, false);
|
|
|
|
return ret;
|
2018-01-05 08:09:05 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct ccs811_data ccs811_driver;
|
|
|
|
|
2019-06-11 14:20:32 -05:00
|
|
|
DEVICE_AND_API_INIT(ccs811, DT_INST_0_AMS_CCS811_LABEL, ccs811_init, &ccs811_driver,
|
2018-01-05 08:09:05 -08:00
|
|
|
NULL, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,
|
|
|
|
&ccs811_driver_api);
|