18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ccs811.c - Support for AMS CCS811 VOC Sensor 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017 Narcisa Vasile <narcisaanamaria12@gmail.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Datasheet: ams.com/content/download/951091/2269479/CCS811_DS000459_3-00.pdf 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * IIO driver for AMS CCS811 (I2C address 0x5A/0x5B set by ADDR Low/High) 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * TODO: 128c2ecf20Sopenharmony_ci * 1. Make the drive mode selectable form userspace 138c2ecf20Sopenharmony_ci * 2. Add support for interrupts 148c2ecf20Sopenharmony_ci * 3. Adjust time to wait for data to be ready based on selected operation mode 158c2ecf20Sopenharmony_ci * 4. Read error register and put the information in logs 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/delay.h> 198c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 208c2ecf20Sopenharmony_ci#include <linux/i2c.h> 218c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 228c2ecf20Sopenharmony_ci#include <linux/iio/buffer.h> 238c2ecf20Sopenharmony_ci#include <linux/iio/trigger.h> 248c2ecf20Sopenharmony_ci#include <linux/iio/triggered_buffer.h> 258c2ecf20Sopenharmony_ci#include <linux/iio/trigger_consumer.h> 268c2ecf20Sopenharmony_ci#include <linux/module.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define CCS811_STATUS 0x00 298c2ecf20Sopenharmony_ci#define CCS811_MEAS_MODE 0x01 308c2ecf20Sopenharmony_ci#define CCS811_ALG_RESULT_DATA 0x02 318c2ecf20Sopenharmony_ci#define CCS811_RAW_DATA 0x03 328c2ecf20Sopenharmony_ci#define CCS811_HW_ID 0x20 338c2ecf20Sopenharmony_ci#define CCS811_HW_ID_VALUE 0x81 348c2ecf20Sopenharmony_ci#define CCS811_HW_VERSION 0x21 358c2ecf20Sopenharmony_ci#define CCS811_HW_VERSION_VALUE 0x10 368c2ecf20Sopenharmony_ci#define CCS811_HW_VERSION_MASK 0xF0 378c2ecf20Sopenharmony_ci#define CCS811_ERR 0xE0 388c2ecf20Sopenharmony_ci/* Used to transition from boot to application mode */ 398c2ecf20Sopenharmony_ci#define CCS811_APP_START 0xF4 408c2ecf20Sopenharmony_ci#define CCS811_SW_RESET 0xFF 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* Status register flags */ 438c2ecf20Sopenharmony_ci#define CCS811_STATUS_ERROR BIT(0) 448c2ecf20Sopenharmony_ci#define CCS811_STATUS_DATA_READY BIT(3) 458c2ecf20Sopenharmony_ci#define CCS811_STATUS_APP_VALID_MASK BIT(4) 468c2ecf20Sopenharmony_ci#define CCS811_STATUS_APP_VALID_LOADED BIT(4) 478c2ecf20Sopenharmony_ci/* 488c2ecf20Sopenharmony_ci * Value of FW_MODE bit of STATUS register describes the sensor's state: 498c2ecf20Sopenharmony_ci * 0: Firmware is in boot mode, this allows new firmware to be loaded 508c2ecf20Sopenharmony_ci * 1: Firmware is in application mode. CCS811 is ready to take ADC measurements 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_ci#define CCS811_STATUS_FW_MODE_MASK BIT(7) 538c2ecf20Sopenharmony_ci#define CCS811_STATUS_FW_MODE_APPLICATION BIT(7) 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* Measurement modes */ 568c2ecf20Sopenharmony_ci#define CCS811_MODE_IDLE 0x00 578c2ecf20Sopenharmony_ci#define CCS811_MODE_IAQ_1SEC 0x10 588c2ecf20Sopenharmony_ci#define CCS811_MODE_IAQ_10SEC 0x20 598c2ecf20Sopenharmony_ci#define CCS811_MODE_IAQ_60SEC 0x30 608c2ecf20Sopenharmony_ci#define CCS811_MODE_RAW_DATA 0x40 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define CCS811_MEAS_MODE_INTERRUPT BIT(3) 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define CCS811_VOLTAGE_MASK 0x3FF 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistruct ccs811_reading { 678c2ecf20Sopenharmony_ci __be16 co2; 688c2ecf20Sopenharmony_ci __be16 voc; 698c2ecf20Sopenharmony_ci u8 status; 708c2ecf20Sopenharmony_ci u8 error; 718c2ecf20Sopenharmony_ci __be16 raw_data; 728c2ecf20Sopenharmony_ci} __attribute__((__packed__)); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistruct ccs811_data { 758c2ecf20Sopenharmony_ci struct i2c_client *client; 768c2ecf20Sopenharmony_ci struct mutex lock; /* Protect readings */ 778c2ecf20Sopenharmony_ci struct ccs811_reading buffer; 788c2ecf20Sopenharmony_ci struct iio_trigger *drdy_trig; 798c2ecf20Sopenharmony_ci struct gpio_desc *wakeup_gpio; 808c2ecf20Sopenharmony_ci bool drdy_trig_on; 818c2ecf20Sopenharmony_ci /* Ensures correct alignment of timestamp if present */ 828c2ecf20Sopenharmony_ci struct { 838c2ecf20Sopenharmony_ci s16 channels[2]; 848c2ecf20Sopenharmony_ci s64 ts __aligned(8); 858c2ecf20Sopenharmony_ci } scan; 868c2ecf20Sopenharmony_ci}; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic const struct iio_chan_spec ccs811_channels[] = { 898c2ecf20Sopenharmony_ci { 908c2ecf20Sopenharmony_ci .type = IIO_CURRENT, 918c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 928c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 938c2ecf20Sopenharmony_ci .scan_index = -1, 948c2ecf20Sopenharmony_ci }, { 958c2ecf20Sopenharmony_ci .type = IIO_VOLTAGE, 968c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 978c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 988c2ecf20Sopenharmony_ci .scan_index = -1, 998c2ecf20Sopenharmony_ci }, { 1008c2ecf20Sopenharmony_ci .type = IIO_CONCENTRATION, 1018c2ecf20Sopenharmony_ci .channel2 = IIO_MOD_CO2, 1028c2ecf20Sopenharmony_ci .modified = 1, 1038c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1048c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 1058c2ecf20Sopenharmony_ci .scan_index = 0, 1068c2ecf20Sopenharmony_ci .scan_type = { 1078c2ecf20Sopenharmony_ci .sign = 'u', 1088c2ecf20Sopenharmony_ci .realbits = 16, 1098c2ecf20Sopenharmony_ci .storagebits = 16, 1108c2ecf20Sopenharmony_ci .endianness = IIO_BE, 1118c2ecf20Sopenharmony_ci }, 1128c2ecf20Sopenharmony_ci }, { 1138c2ecf20Sopenharmony_ci .type = IIO_CONCENTRATION, 1148c2ecf20Sopenharmony_ci .channel2 = IIO_MOD_VOC, 1158c2ecf20Sopenharmony_ci .modified = 1, 1168c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1178c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 1188c2ecf20Sopenharmony_ci .scan_index = 1, 1198c2ecf20Sopenharmony_ci .scan_type = { 1208c2ecf20Sopenharmony_ci .sign = 'u', 1218c2ecf20Sopenharmony_ci .realbits = 16, 1228c2ecf20Sopenharmony_ci .storagebits = 16, 1238c2ecf20Sopenharmony_ci .endianness = IIO_BE, 1248c2ecf20Sopenharmony_ci }, 1258c2ecf20Sopenharmony_ci }, 1268c2ecf20Sopenharmony_ci IIO_CHAN_SOFT_TIMESTAMP(2), 1278c2ecf20Sopenharmony_ci}; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* 1308c2ecf20Sopenharmony_ci * The CCS811 powers-up in boot mode. A setup write to CCS811_APP_START will 1318c2ecf20Sopenharmony_ci * transition the sensor to application mode. 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_cistatic int ccs811_start_sensor_application(struct i2c_client *client) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci int ret; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(client, CCS811_STATUS); 1388c2ecf20Sopenharmony_ci if (ret < 0) 1398c2ecf20Sopenharmony_ci return ret; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if ((ret & CCS811_STATUS_FW_MODE_APPLICATION)) 1428c2ecf20Sopenharmony_ci return 0; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if ((ret & CCS811_STATUS_APP_VALID_MASK) != 1458c2ecf20Sopenharmony_ci CCS811_STATUS_APP_VALID_LOADED) 1468c2ecf20Sopenharmony_ci return -EIO; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte(client, CCS811_APP_START); 1498c2ecf20Sopenharmony_ci if (ret < 0) 1508c2ecf20Sopenharmony_ci return ret; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(client, CCS811_STATUS); 1538c2ecf20Sopenharmony_ci if (ret < 0) 1548c2ecf20Sopenharmony_ci return ret; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if ((ret & CCS811_STATUS_FW_MODE_MASK) != 1578c2ecf20Sopenharmony_ci CCS811_STATUS_FW_MODE_APPLICATION) { 1588c2ecf20Sopenharmony_ci dev_err(&client->dev, "Application failed to start. Sensor is still in boot mode.\n"); 1598c2ecf20Sopenharmony_ci return -EIO; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic int ccs811_setup(struct i2c_client *client) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci int ret; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci ret = ccs811_start_sensor_application(client); 1708c2ecf20Sopenharmony_ci if (ret < 0) 1718c2ecf20Sopenharmony_ci return ret; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return i2c_smbus_write_byte_data(client, CCS811_MEAS_MODE, 1748c2ecf20Sopenharmony_ci CCS811_MODE_IAQ_1SEC); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic void ccs811_set_wakeup(struct ccs811_data *data, bool enable) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci if (!data->wakeup_gpio) 1808c2ecf20Sopenharmony_ci return; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci gpiod_set_value(data->wakeup_gpio, enable); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (enable) 1858c2ecf20Sopenharmony_ci usleep_range(50, 60); 1868c2ecf20Sopenharmony_ci else 1878c2ecf20Sopenharmony_ci usleep_range(20, 30); 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic int ccs811_get_measurement(struct ccs811_data *data) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci int ret, tries = 11; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci ccs811_set_wakeup(data, true); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* Maximum waiting time: 1s, as measurements are made every second */ 1978c2ecf20Sopenharmony_ci while (tries-- > 0) { 1988c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, CCS811_STATUS); 1998c2ecf20Sopenharmony_ci if (ret < 0) 2008c2ecf20Sopenharmony_ci return ret; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if ((ret & CCS811_STATUS_DATA_READY) || tries == 0) 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci msleep(100); 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci if (!(ret & CCS811_STATUS_DATA_READY)) 2078c2ecf20Sopenharmony_ci return -EIO; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci ret = i2c_smbus_read_i2c_block_data(data->client, 2108c2ecf20Sopenharmony_ci CCS811_ALG_RESULT_DATA, 8, 2118c2ecf20Sopenharmony_ci (char *)&data->buffer); 2128c2ecf20Sopenharmony_ci ccs811_set_wakeup(data, false); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci return ret; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int ccs811_read_raw(struct iio_dev *indio_dev, 2188c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 2198c2ecf20Sopenharmony_ci int *val, int *val2, long mask) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct ccs811_data *data = iio_priv(indio_dev); 2228c2ecf20Sopenharmony_ci int ret; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci switch (mask) { 2258c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 2268c2ecf20Sopenharmony_ci ret = iio_device_claim_direct_mode(indio_dev); 2278c2ecf20Sopenharmony_ci if (ret) 2288c2ecf20Sopenharmony_ci return ret; 2298c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 2308c2ecf20Sopenharmony_ci ret = ccs811_get_measurement(data); 2318c2ecf20Sopenharmony_ci if (ret < 0) { 2328c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 2338c2ecf20Sopenharmony_ci iio_device_release_direct_mode(indio_dev); 2348c2ecf20Sopenharmony_ci return ret; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci switch (chan->type) { 2388c2ecf20Sopenharmony_ci case IIO_VOLTAGE: 2398c2ecf20Sopenharmony_ci *val = be16_to_cpu(data->buffer.raw_data) & 2408c2ecf20Sopenharmony_ci CCS811_VOLTAGE_MASK; 2418c2ecf20Sopenharmony_ci ret = IIO_VAL_INT; 2428c2ecf20Sopenharmony_ci break; 2438c2ecf20Sopenharmony_ci case IIO_CURRENT: 2448c2ecf20Sopenharmony_ci *val = be16_to_cpu(data->buffer.raw_data) >> 10; 2458c2ecf20Sopenharmony_ci ret = IIO_VAL_INT; 2468c2ecf20Sopenharmony_ci break; 2478c2ecf20Sopenharmony_ci case IIO_CONCENTRATION: 2488c2ecf20Sopenharmony_ci switch (chan->channel2) { 2498c2ecf20Sopenharmony_ci case IIO_MOD_CO2: 2508c2ecf20Sopenharmony_ci *val = be16_to_cpu(data->buffer.co2); 2518c2ecf20Sopenharmony_ci ret = IIO_VAL_INT; 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci case IIO_MOD_VOC: 2548c2ecf20Sopenharmony_ci *val = be16_to_cpu(data->buffer.voc); 2558c2ecf20Sopenharmony_ci ret = IIO_VAL_INT; 2568c2ecf20Sopenharmony_ci break; 2578c2ecf20Sopenharmony_ci default: 2588c2ecf20Sopenharmony_ci ret = -EINVAL; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci default: 2628c2ecf20Sopenharmony_ci ret = -EINVAL; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 2658c2ecf20Sopenharmony_ci iio_device_release_direct_mode(indio_dev); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return ret; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 2708c2ecf20Sopenharmony_ci switch (chan->type) { 2718c2ecf20Sopenharmony_ci case IIO_VOLTAGE: 2728c2ecf20Sopenharmony_ci *val = 1; 2738c2ecf20Sopenharmony_ci *val2 = 612903; 2748c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 2758c2ecf20Sopenharmony_ci case IIO_CURRENT: 2768c2ecf20Sopenharmony_ci *val = 0; 2778c2ecf20Sopenharmony_ci *val2 = 1000; 2788c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 2798c2ecf20Sopenharmony_ci case IIO_CONCENTRATION: 2808c2ecf20Sopenharmony_ci switch (chan->channel2) { 2818c2ecf20Sopenharmony_ci case IIO_MOD_CO2: 2828c2ecf20Sopenharmony_ci *val = 0; 2838c2ecf20Sopenharmony_ci *val2 = 100; 2848c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 2858c2ecf20Sopenharmony_ci case IIO_MOD_VOC: 2868c2ecf20Sopenharmony_ci *val = 0; 2878c2ecf20Sopenharmony_ci *val2 = 100; 2888c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_NANO; 2898c2ecf20Sopenharmony_ci default: 2908c2ecf20Sopenharmony_ci return -EINVAL; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci default: 2938c2ecf20Sopenharmony_ci return -EINVAL; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci default: 2968c2ecf20Sopenharmony_ci return -EINVAL; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic const struct iio_info ccs811_info = { 3018c2ecf20Sopenharmony_ci .read_raw = ccs811_read_raw, 3028c2ecf20Sopenharmony_ci}; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic int ccs811_set_trigger_state(struct iio_trigger *trig, 3058c2ecf20Sopenharmony_ci bool state) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); 3088c2ecf20Sopenharmony_ci struct ccs811_data *data = iio_priv(indio_dev); 3098c2ecf20Sopenharmony_ci int ret; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, CCS811_MEAS_MODE); 3128c2ecf20Sopenharmony_ci if (ret < 0) 3138c2ecf20Sopenharmony_ci return ret; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (state) 3168c2ecf20Sopenharmony_ci ret |= CCS811_MEAS_MODE_INTERRUPT; 3178c2ecf20Sopenharmony_ci else 3188c2ecf20Sopenharmony_ci ret &= ~CCS811_MEAS_MODE_INTERRUPT; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci data->drdy_trig_on = state; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci return i2c_smbus_write_byte_data(data->client, CCS811_MEAS_MODE, ret); 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic const struct iio_trigger_ops ccs811_trigger_ops = { 3268c2ecf20Sopenharmony_ci .set_trigger_state = ccs811_set_trigger_state, 3278c2ecf20Sopenharmony_ci}; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic irqreturn_t ccs811_trigger_handler(int irq, void *p) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct iio_poll_func *pf = p; 3328c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = pf->indio_dev; 3338c2ecf20Sopenharmony_ci struct ccs811_data *data = iio_priv(indio_dev); 3348c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 3358c2ecf20Sopenharmony_ci int ret; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci ret = i2c_smbus_read_i2c_block_data(client, CCS811_ALG_RESULT_DATA, 3388c2ecf20Sopenharmony_ci sizeof(data->scan.channels), 3398c2ecf20Sopenharmony_ci (u8 *)data->scan.channels); 3408c2ecf20Sopenharmony_ci if (ret != 4) { 3418c2ecf20Sopenharmony_ci dev_err(&client->dev, "cannot read sensor data\n"); 3428c2ecf20Sopenharmony_ci goto err; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, 3468c2ecf20Sopenharmony_ci iio_get_time_ns(indio_dev)); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cierr: 3498c2ecf20Sopenharmony_ci iio_trigger_notify_done(indio_dev->trig); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic irqreturn_t ccs811_data_rdy_trigger_poll(int irq, void *private) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = private; 3578c2ecf20Sopenharmony_ci struct ccs811_data *data = iio_priv(indio_dev); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (data->drdy_trig_on) 3608c2ecf20Sopenharmony_ci iio_trigger_poll(data->drdy_trig); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic int ccs811_reset(struct i2c_client *client) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci struct gpio_desc *reset_gpio; 3688c2ecf20Sopenharmony_ci int ret; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", 3718c2ecf20Sopenharmony_ci GPIOD_OUT_LOW); 3728c2ecf20Sopenharmony_ci if (IS_ERR(reset_gpio)) 3738c2ecf20Sopenharmony_ci return PTR_ERR(reset_gpio); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci /* Try to reset using nRESET pin if available else do SW reset */ 3768c2ecf20Sopenharmony_ci if (reset_gpio) { 3778c2ecf20Sopenharmony_ci gpiod_set_value(reset_gpio, 1); 3788c2ecf20Sopenharmony_ci usleep_range(20, 30); 3798c2ecf20Sopenharmony_ci gpiod_set_value(reset_gpio, 0); 3808c2ecf20Sopenharmony_ci } else { 3818c2ecf20Sopenharmony_ci /* 3828c2ecf20Sopenharmony_ci * As per the datasheet, this sequence of values needs to be 3838c2ecf20Sopenharmony_ci * written to the SW_RESET register for triggering the soft 3848c2ecf20Sopenharmony_ci * reset in the device and placing it in boot mode. 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_ci static const u8 reset_seq[] = { 3878c2ecf20Sopenharmony_ci 0x11, 0xE5, 0x72, 0x8A, 3888c2ecf20Sopenharmony_ci }; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci ret = i2c_smbus_write_i2c_block_data(client, CCS811_SW_RESET, 3918c2ecf20Sopenharmony_ci sizeof(reset_seq), reset_seq); 3928c2ecf20Sopenharmony_ci if (ret < 0) { 3938c2ecf20Sopenharmony_ci dev_err(&client->dev, "Failed to reset sensor\n"); 3948c2ecf20Sopenharmony_ci return ret; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* tSTART delay required after reset */ 3998c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci return 0; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic int ccs811_probe(struct i2c_client *client, 4058c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 4088c2ecf20Sopenharmony_ci struct ccs811_data *data; 4098c2ecf20Sopenharmony_ci int ret; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE 4128c2ecf20Sopenharmony_ci | I2C_FUNC_SMBUS_BYTE_DATA 4138c2ecf20Sopenharmony_ci | I2C_FUNC_SMBUS_READ_I2C_BLOCK)) 4148c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 4178c2ecf20Sopenharmony_ci if (!indio_dev) 4188c2ecf20Sopenharmony_ci return -ENOMEM; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci data = iio_priv(indio_dev); 4218c2ecf20Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 4228c2ecf20Sopenharmony_ci data->client = client; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci data->wakeup_gpio = devm_gpiod_get_optional(&client->dev, "wakeup", 4258c2ecf20Sopenharmony_ci GPIOD_OUT_HIGH); 4268c2ecf20Sopenharmony_ci if (IS_ERR(data->wakeup_gpio)) 4278c2ecf20Sopenharmony_ci return PTR_ERR(data->wakeup_gpio); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci ccs811_set_wakeup(data, true); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci ret = ccs811_reset(client); 4328c2ecf20Sopenharmony_ci if (ret) { 4338c2ecf20Sopenharmony_ci ccs811_set_wakeup(data, false); 4348c2ecf20Sopenharmony_ci return ret; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* Check hardware id (should be 0x81 for this family of devices) */ 4388c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(client, CCS811_HW_ID); 4398c2ecf20Sopenharmony_ci if (ret < 0) { 4408c2ecf20Sopenharmony_ci ccs811_set_wakeup(data, false); 4418c2ecf20Sopenharmony_ci return ret; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (ret != CCS811_HW_ID_VALUE) { 4458c2ecf20Sopenharmony_ci dev_err(&client->dev, "hardware id doesn't match CCS81x\n"); 4468c2ecf20Sopenharmony_ci ccs811_set_wakeup(data, false); 4478c2ecf20Sopenharmony_ci return -ENODEV; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(client, CCS811_HW_VERSION); 4518c2ecf20Sopenharmony_ci if (ret < 0) { 4528c2ecf20Sopenharmony_ci ccs811_set_wakeup(data, false); 4538c2ecf20Sopenharmony_ci return ret; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if ((ret & CCS811_HW_VERSION_MASK) != CCS811_HW_VERSION_VALUE) { 4578c2ecf20Sopenharmony_ci dev_err(&client->dev, "no CCS811 sensor\n"); 4588c2ecf20Sopenharmony_ci ccs811_set_wakeup(data, false); 4598c2ecf20Sopenharmony_ci return -ENODEV; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci ret = ccs811_setup(client); 4638c2ecf20Sopenharmony_ci if (ret < 0) { 4648c2ecf20Sopenharmony_ci ccs811_set_wakeup(data, false); 4658c2ecf20Sopenharmony_ci return ret; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci ccs811_set_wakeup(data, false); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci mutex_init(&data->lock); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci indio_dev->name = id->name; 4738c2ecf20Sopenharmony_ci indio_dev->info = &ccs811_info; 4748c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci indio_dev->channels = ccs811_channels; 4778c2ecf20Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(ccs811_channels); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (client->irq > 0) { 4808c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&client->dev, client->irq, 4818c2ecf20Sopenharmony_ci ccs811_data_rdy_trigger_poll, 4828c2ecf20Sopenharmony_ci NULL, 4838c2ecf20Sopenharmony_ci IRQF_TRIGGER_FALLING | 4848c2ecf20Sopenharmony_ci IRQF_ONESHOT, 4858c2ecf20Sopenharmony_ci "ccs811_irq", indio_dev); 4868c2ecf20Sopenharmony_ci if (ret) { 4878c2ecf20Sopenharmony_ci dev_err(&client->dev, "irq request error %d\n", -ret); 4888c2ecf20Sopenharmony_ci goto err_poweroff; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci data->drdy_trig = devm_iio_trigger_alloc(&client->dev, 4928c2ecf20Sopenharmony_ci "%s-dev%d", 4938c2ecf20Sopenharmony_ci indio_dev->name, 4948c2ecf20Sopenharmony_ci indio_dev->id); 4958c2ecf20Sopenharmony_ci if (!data->drdy_trig) { 4968c2ecf20Sopenharmony_ci ret = -ENOMEM; 4978c2ecf20Sopenharmony_ci goto err_poweroff; 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci data->drdy_trig->dev.parent = &client->dev; 5018c2ecf20Sopenharmony_ci data->drdy_trig->ops = &ccs811_trigger_ops; 5028c2ecf20Sopenharmony_ci iio_trigger_set_drvdata(data->drdy_trig, indio_dev); 5038c2ecf20Sopenharmony_ci ret = iio_trigger_register(data->drdy_trig); 5048c2ecf20Sopenharmony_ci if (ret) 5058c2ecf20Sopenharmony_ci goto err_poweroff; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci indio_dev->trig = iio_trigger_get(data->drdy_trig); 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci ret = iio_triggered_buffer_setup(indio_dev, NULL, 5118c2ecf20Sopenharmony_ci ccs811_trigger_handler, NULL); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci if (ret < 0) { 5148c2ecf20Sopenharmony_ci dev_err(&client->dev, "triggered buffer setup failed\n"); 5158c2ecf20Sopenharmony_ci goto err_trigger_unregister; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci ret = iio_device_register(indio_dev); 5198c2ecf20Sopenharmony_ci if (ret < 0) { 5208c2ecf20Sopenharmony_ci dev_err(&client->dev, "unable to register iio device\n"); 5218c2ecf20Sopenharmony_ci goto err_buffer_cleanup; 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci return 0; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cierr_buffer_cleanup: 5268c2ecf20Sopenharmony_ci iio_triggered_buffer_cleanup(indio_dev); 5278c2ecf20Sopenharmony_cierr_trigger_unregister: 5288c2ecf20Sopenharmony_ci if (data->drdy_trig) 5298c2ecf20Sopenharmony_ci iio_trigger_unregister(data->drdy_trig); 5308c2ecf20Sopenharmony_cierr_poweroff: 5318c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, CCS811_MEAS_MODE, CCS811_MODE_IDLE); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci return ret; 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cistatic int ccs811_remove(struct i2c_client *client) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(client); 5398c2ecf20Sopenharmony_ci struct ccs811_data *data = iio_priv(indio_dev); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci iio_device_unregister(indio_dev); 5428c2ecf20Sopenharmony_ci iio_triggered_buffer_cleanup(indio_dev); 5438c2ecf20Sopenharmony_ci if (data->drdy_trig) 5448c2ecf20Sopenharmony_ci iio_trigger_unregister(data->drdy_trig); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci return i2c_smbus_write_byte_data(client, CCS811_MEAS_MODE, 5478c2ecf20Sopenharmony_ci CCS811_MODE_IDLE); 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic const struct i2c_device_id ccs811_id[] = { 5518c2ecf20Sopenharmony_ci {"ccs811", 0}, 5528c2ecf20Sopenharmony_ci { } 5538c2ecf20Sopenharmony_ci}; 5548c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ccs811_id); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cistatic const struct of_device_id ccs811_dt_ids[] = { 5578c2ecf20Sopenharmony_ci { .compatible = "ams,ccs811" }, 5588c2ecf20Sopenharmony_ci { } 5598c2ecf20Sopenharmony_ci}; 5608c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ccs811_dt_ids); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_cistatic struct i2c_driver ccs811_driver = { 5638c2ecf20Sopenharmony_ci .driver = { 5648c2ecf20Sopenharmony_ci .name = "ccs811", 5658c2ecf20Sopenharmony_ci .of_match_table = ccs811_dt_ids, 5668c2ecf20Sopenharmony_ci }, 5678c2ecf20Sopenharmony_ci .probe = ccs811_probe, 5688c2ecf20Sopenharmony_ci .remove = ccs811_remove, 5698c2ecf20Sopenharmony_ci .id_table = ccs811_id, 5708c2ecf20Sopenharmony_ci}; 5718c2ecf20Sopenharmony_cimodule_i2c_driver(ccs811_driver); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ciMODULE_AUTHOR("Narcisa Vasile <narcisaanamaria12@gmail.com>"); 5748c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("CCS811 volatile organic compounds sensor"); 5758c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 576