162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ccs811.c - Support for AMS CCS811 VOC Sensor
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2017 Narcisa Vasile <narcisaanamaria12@gmail.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Datasheet: ams.com/content/download/951091/2269479/CCS811_DS000459_3-00.pdf
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * IIO driver for AMS CCS811 (I2C address 0x5A/0x5B set by ADDR Low/High)
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * TODO:
1262306a36Sopenharmony_ci * 1. Make the drive mode selectable form userspace
1362306a36Sopenharmony_ci * 2. Add support for interrupts
1462306a36Sopenharmony_ci * 3. Adjust time to wait for data to be ready based on selected operation mode
1562306a36Sopenharmony_ci * 4. Read error register and put the information in logs
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/delay.h>
1962306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
2062306a36Sopenharmony_ci#include <linux/i2c.h>
2162306a36Sopenharmony_ci#include <linux/iio/iio.h>
2262306a36Sopenharmony_ci#include <linux/iio/buffer.h>
2362306a36Sopenharmony_ci#include <linux/iio/trigger.h>
2462306a36Sopenharmony_ci#include <linux/iio/triggered_buffer.h>
2562306a36Sopenharmony_ci#include <linux/iio/trigger_consumer.h>
2662306a36Sopenharmony_ci#include <linux/module.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define CCS811_STATUS		0x00
2962306a36Sopenharmony_ci#define CCS811_MEAS_MODE	0x01
3062306a36Sopenharmony_ci#define CCS811_ALG_RESULT_DATA	0x02
3162306a36Sopenharmony_ci#define CCS811_RAW_DATA		0x03
3262306a36Sopenharmony_ci#define CCS811_HW_ID		0x20
3362306a36Sopenharmony_ci#define CCS811_HW_ID_VALUE	0x81
3462306a36Sopenharmony_ci#define CCS811_HW_VERSION	0x21
3562306a36Sopenharmony_ci#define CCS811_HW_VERSION_VALUE	0x10
3662306a36Sopenharmony_ci#define CCS811_HW_VERSION_MASK	0xF0
3762306a36Sopenharmony_ci#define CCS811_ERR		0xE0
3862306a36Sopenharmony_ci/* Used to transition from boot to application mode */
3962306a36Sopenharmony_ci#define CCS811_APP_START	0xF4
4062306a36Sopenharmony_ci#define CCS811_SW_RESET		0xFF
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* Status register flags */
4362306a36Sopenharmony_ci#define CCS811_STATUS_ERROR		BIT(0)
4462306a36Sopenharmony_ci#define CCS811_STATUS_DATA_READY	BIT(3)
4562306a36Sopenharmony_ci#define CCS811_STATUS_APP_VALID_MASK	BIT(4)
4662306a36Sopenharmony_ci#define CCS811_STATUS_APP_VALID_LOADED	BIT(4)
4762306a36Sopenharmony_ci/*
4862306a36Sopenharmony_ci * Value of FW_MODE bit of STATUS register describes the sensor's state:
4962306a36Sopenharmony_ci * 0: Firmware is in boot mode, this allows new firmware to be loaded
5062306a36Sopenharmony_ci * 1: Firmware is in application mode. CCS811 is ready to take ADC measurements
5162306a36Sopenharmony_ci */
5262306a36Sopenharmony_ci#define CCS811_STATUS_FW_MODE_MASK	BIT(7)
5362306a36Sopenharmony_ci#define CCS811_STATUS_FW_MODE_APPLICATION	BIT(7)
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/* Measurement modes */
5662306a36Sopenharmony_ci#define CCS811_MODE_IDLE	0x00
5762306a36Sopenharmony_ci#define CCS811_MODE_IAQ_1SEC	0x10
5862306a36Sopenharmony_ci#define CCS811_MODE_IAQ_10SEC	0x20
5962306a36Sopenharmony_ci#define CCS811_MODE_IAQ_60SEC	0x30
6062306a36Sopenharmony_ci#define CCS811_MODE_RAW_DATA	0x40
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci#define CCS811_MEAS_MODE_INTERRUPT	BIT(3)
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#define CCS811_VOLTAGE_MASK	0x3FF
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistruct ccs811_reading {
6762306a36Sopenharmony_ci	__be16 co2;
6862306a36Sopenharmony_ci	__be16 voc;
6962306a36Sopenharmony_ci	u8 status;
7062306a36Sopenharmony_ci	u8 error;
7162306a36Sopenharmony_ci	__be16 raw_data;
7262306a36Sopenharmony_ci} __attribute__((__packed__));
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistruct ccs811_data {
7562306a36Sopenharmony_ci	struct i2c_client *client;
7662306a36Sopenharmony_ci	struct mutex lock; /* Protect readings */
7762306a36Sopenharmony_ci	struct ccs811_reading buffer;
7862306a36Sopenharmony_ci	struct iio_trigger *drdy_trig;
7962306a36Sopenharmony_ci	struct gpio_desc *wakeup_gpio;
8062306a36Sopenharmony_ci	bool drdy_trig_on;
8162306a36Sopenharmony_ci	/* Ensures correct alignment of timestamp if present */
8262306a36Sopenharmony_ci	struct {
8362306a36Sopenharmony_ci		s16 channels[2];
8462306a36Sopenharmony_ci		s64 ts __aligned(8);
8562306a36Sopenharmony_ci	} scan;
8662306a36Sopenharmony_ci};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic const struct iio_chan_spec ccs811_channels[] = {
8962306a36Sopenharmony_ci	{
9062306a36Sopenharmony_ci		.type = IIO_CURRENT,
9162306a36Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
9262306a36Sopenharmony_ci				      BIT(IIO_CHAN_INFO_SCALE),
9362306a36Sopenharmony_ci		.scan_index = -1,
9462306a36Sopenharmony_ci	}, {
9562306a36Sopenharmony_ci		.type = IIO_VOLTAGE,
9662306a36Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
9762306a36Sopenharmony_ci				      BIT(IIO_CHAN_INFO_SCALE),
9862306a36Sopenharmony_ci		.scan_index = -1,
9962306a36Sopenharmony_ci	}, {
10062306a36Sopenharmony_ci		.type = IIO_CONCENTRATION,
10162306a36Sopenharmony_ci		.channel2 = IIO_MOD_CO2,
10262306a36Sopenharmony_ci		.modified = 1,
10362306a36Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
10462306a36Sopenharmony_ci				      BIT(IIO_CHAN_INFO_SCALE),
10562306a36Sopenharmony_ci		.scan_index = 0,
10662306a36Sopenharmony_ci		.scan_type = {
10762306a36Sopenharmony_ci			.sign = 'u',
10862306a36Sopenharmony_ci			.realbits = 16,
10962306a36Sopenharmony_ci			.storagebits = 16,
11062306a36Sopenharmony_ci			.endianness = IIO_BE,
11162306a36Sopenharmony_ci		},
11262306a36Sopenharmony_ci	}, {
11362306a36Sopenharmony_ci		.type = IIO_CONCENTRATION,
11462306a36Sopenharmony_ci		.channel2 = IIO_MOD_VOC,
11562306a36Sopenharmony_ci		.modified = 1,
11662306a36Sopenharmony_ci		.info_mask_separate =  BIT(IIO_CHAN_INFO_RAW) |
11762306a36Sopenharmony_ci				       BIT(IIO_CHAN_INFO_SCALE),
11862306a36Sopenharmony_ci		.scan_index = 1,
11962306a36Sopenharmony_ci		.scan_type = {
12062306a36Sopenharmony_ci			.sign = 'u',
12162306a36Sopenharmony_ci			.realbits = 16,
12262306a36Sopenharmony_ci			.storagebits = 16,
12362306a36Sopenharmony_ci			.endianness = IIO_BE,
12462306a36Sopenharmony_ci		},
12562306a36Sopenharmony_ci	},
12662306a36Sopenharmony_ci	IIO_CHAN_SOFT_TIMESTAMP(2),
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/*
13062306a36Sopenharmony_ci * The CCS811 powers-up in boot mode. A setup write to CCS811_APP_START will
13162306a36Sopenharmony_ci * transition the sensor to application mode.
13262306a36Sopenharmony_ci */
13362306a36Sopenharmony_cistatic int ccs811_start_sensor_application(struct i2c_client *client)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	int ret;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	ret = i2c_smbus_read_byte_data(client, CCS811_STATUS);
13862306a36Sopenharmony_ci	if (ret < 0)
13962306a36Sopenharmony_ci		return ret;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	if ((ret & CCS811_STATUS_FW_MODE_APPLICATION))
14262306a36Sopenharmony_ci		return 0;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	if ((ret & CCS811_STATUS_APP_VALID_MASK) !=
14562306a36Sopenharmony_ci	    CCS811_STATUS_APP_VALID_LOADED)
14662306a36Sopenharmony_ci		return -EIO;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	ret = i2c_smbus_write_byte(client, CCS811_APP_START);
14962306a36Sopenharmony_ci	if (ret < 0)
15062306a36Sopenharmony_ci		return ret;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	ret = i2c_smbus_read_byte_data(client, CCS811_STATUS);
15362306a36Sopenharmony_ci	if (ret < 0)
15462306a36Sopenharmony_ci		return ret;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if ((ret & CCS811_STATUS_FW_MODE_MASK) !=
15762306a36Sopenharmony_ci	    CCS811_STATUS_FW_MODE_APPLICATION) {
15862306a36Sopenharmony_ci		dev_err(&client->dev, "Application failed to start. Sensor is still in boot mode.\n");
15962306a36Sopenharmony_ci		return -EIO;
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	return 0;
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic int ccs811_setup(struct i2c_client *client)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	int ret;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	ret = ccs811_start_sensor_application(client);
17062306a36Sopenharmony_ci	if (ret < 0)
17162306a36Sopenharmony_ci		return ret;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	return i2c_smbus_write_byte_data(client, CCS811_MEAS_MODE,
17462306a36Sopenharmony_ci					 CCS811_MODE_IAQ_1SEC);
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic void ccs811_set_wakeup(struct ccs811_data *data, bool enable)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	if (!data->wakeup_gpio)
18062306a36Sopenharmony_ci		return;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	gpiod_set_value(data->wakeup_gpio, enable);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (enable)
18562306a36Sopenharmony_ci		usleep_range(50, 60);
18662306a36Sopenharmony_ci	else
18762306a36Sopenharmony_ci		usleep_range(20, 30);
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic int ccs811_get_measurement(struct ccs811_data *data)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	int ret, tries = 11;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	ccs811_set_wakeup(data, true);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	/* Maximum waiting time: 1s, as measurements are made every second */
19762306a36Sopenharmony_ci	while (tries-- > 0) {
19862306a36Sopenharmony_ci		ret = i2c_smbus_read_byte_data(data->client, CCS811_STATUS);
19962306a36Sopenharmony_ci		if (ret < 0)
20062306a36Sopenharmony_ci			return ret;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci		if ((ret & CCS811_STATUS_DATA_READY) || tries == 0)
20362306a36Sopenharmony_ci			break;
20462306a36Sopenharmony_ci		msleep(100);
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci	if (!(ret & CCS811_STATUS_DATA_READY))
20762306a36Sopenharmony_ci		return -EIO;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	ret = i2c_smbus_read_i2c_block_data(data->client,
21062306a36Sopenharmony_ci					    CCS811_ALG_RESULT_DATA, 8,
21162306a36Sopenharmony_ci					    (char *)&data->buffer);
21262306a36Sopenharmony_ci	ccs811_set_wakeup(data, false);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	return ret;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic int ccs811_read_raw(struct iio_dev *indio_dev,
21862306a36Sopenharmony_ci			   struct iio_chan_spec const *chan,
21962306a36Sopenharmony_ci			   int *val, int *val2, long mask)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	struct ccs811_data *data = iio_priv(indio_dev);
22262306a36Sopenharmony_ci	int ret;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	switch (mask) {
22562306a36Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
22662306a36Sopenharmony_ci		ret = iio_device_claim_direct_mode(indio_dev);
22762306a36Sopenharmony_ci		if (ret)
22862306a36Sopenharmony_ci			return ret;
22962306a36Sopenharmony_ci		mutex_lock(&data->lock);
23062306a36Sopenharmony_ci		ret = ccs811_get_measurement(data);
23162306a36Sopenharmony_ci		if (ret < 0) {
23262306a36Sopenharmony_ci			mutex_unlock(&data->lock);
23362306a36Sopenharmony_ci			iio_device_release_direct_mode(indio_dev);
23462306a36Sopenharmony_ci			return ret;
23562306a36Sopenharmony_ci		}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		switch (chan->type) {
23862306a36Sopenharmony_ci		case IIO_VOLTAGE:
23962306a36Sopenharmony_ci			*val = be16_to_cpu(data->buffer.raw_data) &
24062306a36Sopenharmony_ci					   CCS811_VOLTAGE_MASK;
24162306a36Sopenharmony_ci			ret = IIO_VAL_INT;
24262306a36Sopenharmony_ci			break;
24362306a36Sopenharmony_ci		case IIO_CURRENT:
24462306a36Sopenharmony_ci			*val = be16_to_cpu(data->buffer.raw_data) >> 10;
24562306a36Sopenharmony_ci			ret = IIO_VAL_INT;
24662306a36Sopenharmony_ci			break;
24762306a36Sopenharmony_ci		case IIO_CONCENTRATION:
24862306a36Sopenharmony_ci			switch (chan->channel2) {
24962306a36Sopenharmony_ci			case IIO_MOD_CO2:
25062306a36Sopenharmony_ci				*val = be16_to_cpu(data->buffer.co2);
25162306a36Sopenharmony_ci				ret =  IIO_VAL_INT;
25262306a36Sopenharmony_ci				break;
25362306a36Sopenharmony_ci			case IIO_MOD_VOC:
25462306a36Sopenharmony_ci				*val = be16_to_cpu(data->buffer.voc);
25562306a36Sopenharmony_ci				ret = IIO_VAL_INT;
25662306a36Sopenharmony_ci				break;
25762306a36Sopenharmony_ci			default:
25862306a36Sopenharmony_ci				ret = -EINVAL;
25962306a36Sopenharmony_ci			}
26062306a36Sopenharmony_ci			break;
26162306a36Sopenharmony_ci		default:
26262306a36Sopenharmony_ci			ret = -EINVAL;
26362306a36Sopenharmony_ci		}
26462306a36Sopenharmony_ci		mutex_unlock(&data->lock);
26562306a36Sopenharmony_ci		iio_device_release_direct_mode(indio_dev);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci		return ret;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
27062306a36Sopenharmony_ci		switch (chan->type) {
27162306a36Sopenharmony_ci		case IIO_VOLTAGE:
27262306a36Sopenharmony_ci			*val = 1;
27362306a36Sopenharmony_ci			*val2 = 612903;
27462306a36Sopenharmony_ci			return IIO_VAL_INT_PLUS_MICRO;
27562306a36Sopenharmony_ci		case IIO_CURRENT:
27662306a36Sopenharmony_ci			*val = 0;
27762306a36Sopenharmony_ci			*val2 = 1000;
27862306a36Sopenharmony_ci			return IIO_VAL_INT_PLUS_MICRO;
27962306a36Sopenharmony_ci		case IIO_CONCENTRATION:
28062306a36Sopenharmony_ci			switch (chan->channel2) {
28162306a36Sopenharmony_ci			case IIO_MOD_CO2:
28262306a36Sopenharmony_ci				*val = 0;
28362306a36Sopenharmony_ci				*val2 = 100;
28462306a36Sopenharmony_ci				return IIO_VAL_INT_PLUS_MICRO;
28562306a36Sopenharmony_ci			case IIO_MOD_VOC:
28662306a36Sopenharmony_ci				*val = 0;
28762306a36Sopenharmony_ci				*val2 = 100;
28862306a36Sopenharmony_ci				return IIO_VAL_INT_PLUS_NANO;
28962306a36Sopenharmony_ci			default:
29062306a36Sopenharmony_ci				return -EINVAL;
29162306a36Sopenharmony_ci			}
29262306a36Sopenharmony_ci		default:
29362306a36Sopenharmony_ci			return -EINVAL;
29462306a36Sopenharmony_ci		}
29562306a36Sopenharmony_ci	default:
29662306a36Sopenharmony_ci		return -EINVAL;
29762306a36Sopenharmony_ci	}
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic const struct iio_info ccs811_info = {
30162306a36Sopenharmony_ci	.read_raw = ccs811_read_raw,
30262306a36Sopenharmony_ci};
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic int ccs811_set_trigger_state(struct iio_trigger *trig,
30562306a36Sopenharmony_ci				    bool state)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
30862306a36Sopenharmony_ci	struct ccs811_data *data = iio_priv(indio_dev);
30962306a36Sopenharmony_ci	int ret;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	ret = i2c_smbus_read_byte_data(data->client, CCS811_MEAS_MODE);
31262306a36Sopenharmony_ci	if (ret < 0)
31362306a36Sopenharmony_ci		return ret;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	if (state)
31662306a36Sopenharmony_ci		ret |= CCS811_MEAS_MODE_INTERRUPT;
31762306a36Sopenharmony_ci	else
31862306a36Sopenharmony_ci		ret &= ~CCS811_MEAS_MODE_INTERRUPT;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	data->drdy_trig_on = state;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	return i2c_smbus_write_byte_data(data->client, CCS811_MEAS_MODE, ret);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic const struct iio_trigger_ops ccs811_trigger_ops = {
32662306a36Sopenharmony_ci	.set_trigger_state = ccs811_set_trigger_state,
32762306a36Sopenharmony_ci};
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic irqreturn_t ccs811_trigger_handler(int irq, void *p)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	struct iio_poll_func *pf = p;
33262306a36Sopenharmony_ci	struct iio_dev *indio_dev = pf->indio_dev;
33362306a36Sopenharmony_ci	struct ccs811_data *data = iio_priv(indio_dev);
33462306a36Sopenharmony_ci	struct i2c_client *client = data->client;
33562306a36Sopenharmony_ci	int ret;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	ret = i2c_smbus_read_i2c_block_data(client, CCS811_ALG_RESULT_DATA,
33862306a36Sopenharmony_ci					    sizeof(data->scan.channels),
33962306a36Sopenharmony_ci					    (u8 *)data->scan.channels);
34062306a36Sopenharmony_ci	if (ret != 4) {
34162306a36Sopenharmony_ci		dev_err(&client->dev, "cannot read sensor data\n");
34262306a36Sopenharmony_ci		goto err;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,
34662306a36Sopenharmony_ci					   iio_get_time_ns(indio_dev));
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cierr:
34962306a36Sopenharmony_ci	iio_trigger_notify_done(indio_dev->trig);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	return IRQ_HANDLED;
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic irqreturn_t ccs811_data_rdy_trigger_poll(int irq, void *private)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	struct iio_dev *indio_dev = private;
35762306a36Sopenharmony_ci	struct ccs811_data *data = iio_priv(indio_dev);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (data->drdy_trig_on)
36062306a36Sopenharmony_ci		iio_trigger_poll(data->drdy_trig);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	return IRQ_HANDLED;
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic int ccs811_reset(struct i2c_client *client)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	struct gpio_desc *reset_gpio;
36862306a36Sopenharmony_ci	int ret;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
37162306a36Sopenharmony_ci					     GPIOD_OUT_LOW);
37262306a36Sopenharmony_ci	if (IS_ERR(reset_gpio))
37362306a36Sopenharmony_ci		return PTR_ERR(reset_gpio);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	/* Try to reset using nRESET pin if available else do SW reset */
37662306a36Sopenharmony_ci	if (reset_gpio) {
37762306a36Sopenharmony_ci		gpiod_set_value(reset_gpio, 1);
37862306a36Sopenharmony_ci		usleep_range(20, 30);
37962306a36Sopenharmony_ci		gpiod_set_value(reset_gpio, 0);
38062306a36Sopenharmony_ci	} else {
38162306a36Sopenharmony_ci		/*
38262306a36Sopenharmony_ci		 * As per the datasheet, this sequence of values needs to be
38362306a36Sopenharmony_ci		 * written to the SW_RESET register for triggering the soft
38462306a36Sopenharmony_ci		 * reset in the device and placing it in boot mode.
38562306a36Sopenharmony_ci		 */
38662306a36Sopenharmony_ci		static const u8 reset_seq[] = {
38762306a36Sopenharmony_ci			0x11, 0xE5, 0x72, 0x8A,
38862306a36Sopenharmony_ci		};
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci		ret = i2c_smbus_write_i2c_block_data(client, CCS811_SW_RESET,
39162306a36Sopenharmony_ci					     sizeof(reset_seq), reset_seq);
39262306a36Sopenharmony_ci		if (ret < 0) {
39362306a36Sopenharmony_ci			dev_err(&client->dev, "Failed to reset sensor\n");
39462306a36Sopenharmony_ci			return ret;
39562306a36Sopenharmony_ci		}
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	/* tSTART delay required after reset */
39962306a36Sopenharmony_ci	usleep_range(1000, 2000);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	return 0;
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic int ccs811_probe(struct i2c_client *client)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	const struct i2c_device_id *id = i2c_client_get_device_id(client);
40762306a36Sopenharmony_ci	struct iio_dev *indio_dev;
40862306a36Sopenharmony_ci	struct ccs811_data *data;
40962306a36Sopenharmony_ci	int ret;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE
41262306a36Sopenharmony_ci				     | I2C_FUNC_SMBUS_BYTE_DATA
41362306a36Sopenharmony_ci				     | I2C_FUNC_SMBUS_READ_I2C_BLOCK))
41462306a36Sopenharmony_ci		return -EOPNOTSUPP;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
41762306a36Sopenharmony_ci	if (!indio_dev)
41862306a36Sopenharmony_ci		return -ENOMEM;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	data = iio_priv(indio_dev);
42162306a36Sopenharmony_ci	i2c_set_clientdata(client, indio_dev);
42262306a36Sopenharmony_ci	data->client = client;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	data->wakeup_gpio = devm_gpiod_get_optional(&client->dev, "wakeup",
42562306a36Sopenharmony_ci						    GPIOD_OUT_HIGH);
42662306a36Sopenharmony_ci	if (IS_ERR(data->wakeup_gpio))
42762306a36Sopenharmony_ci		return PTR_ERR(data->wakeup_gpio);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	ccs811_set_wakeup(data, true);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	ret = ccs811_reset(client);
43262306a36Sopenharmony_ci	if (ret) {
43362306a36Sopenharmony_ci		ccs811_set_wakeup(data, false);
43462306a36Sopenharmony_ci		return ret;
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	/* Check hardware id (should be 0x81 for this family of devices) */
43862306a36Sopenharmony_ci	ret = i2c_smbus_read_byte_data(client, CCS811_HW_ID);
43962306a36Sopenharmony_ci	if (ret < 0) {
44062306a36Sopenharmony_ci		ccs811_set_wakeup(data, false);
44162306a36Sopenharmony_ci		return ret;
44262306a36Sopenharmony_ci	}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	if (ret != CCS811_HW_ID_VALUE) {
44562306a36Sopenharmony_ci		dev_err(&client->dev, "hardware id doesn't match CCS81x\n");
44662306a36Sopenharmony_ci		ccs811_set_wakeup(data, false);
44762306a36Sopenharmony_ci		return -ENODEV;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	ret = i2c_smbus_read_byte_data(client, CCS811_HW_VERSION);
45162306a36Sopenharmony_ci	if (ret < 0) {
45262306a36Sopenharmony_ci		ccs811_set_wakeup(data, false);
45362306a36Sopenharmony_ci		return ret;
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	if ((ret & CCS811_HW_VERSION_MASK) != CCS811_HW_VERSION_VALUE) {
45762306a36Sopenharmony_ci		dev_err(&client->dev, "no CCS811 sensor\n");
45862306a36Sopenharmony_ci		ccs811_set_wakeup(data, false);
45962306a36Sopenharmony_ci		return -ENODEV;
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	ret = ccs811_setup(client);
46362306a36Sopenharmony_ci	if (ret < 0) {
46462306a36Sopenharmony_ci		ccs811_set_wakeup(data, false);
46562306a36Sopenharmony_ci		return ret;
46662306a36Sopenharmony_ci	}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	ccs811_set_wakeup(data, false);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	mutex_init(&data->lock);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	indio_dev->name = id->name;
47362306a36Sopenharmony_ci	indio_dev->info = &ccs811_info;
47462306a36Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	indio_dev->channels = ccs811_channels;
47762306a36Sopenharmony_ci	indio_dev->num_channels = ARRAY_SIZE(ccs811_channels);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	if (client->irq > 0) {
48062306a36Sopenharmony_ci		ret = devm_request_threaded_irq(&client->dev, client->irq,
48162306a36Sopenharmony_ci						ccs811_data_rdy_trigger_poll,
48262306a36Sopenharmony_ci						NULL,
48362306a36Sopenharmony_ci						IRQF_TRIGGER_FALLING |
48462306a36Sopenharmony_ci						IRQF_ONESHOT,
48562306a36Sopenharmony_ci						"ccs811_irq", indio_dev);
48662306a36Sopenharmony_ci		if (ret) {
48762306a36Sopenharmony_ci			dev_err(&client->dev, "irq request error %d\n", -ret);
48862306a36Sopenharmony_ci			goto err_poweroff;
48962306a36Sopenharmony_ci		}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci		data->drdy_trig = devm_iio_trigger_alloc(&client->dev,
49262306a36Sopenharmony_ci							 "%s-dev%d",
49362306a36Sopenharmony_ci							 indio_dev->name,
49462306a36Sopenharmony_ci							 iio_device_id(indio_dev));
49562306a36Sopenharmony_ci		if (!data->drdy_trig) {
49662306a36Sopenharmony_ci			ret = -ENOMEM;
49762306a36Sopenharmony_ci			goto err_poweroff;
49862306a36Sopenharmony_ci		}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci		data->drdy_trig->ops = &ccs811_trigger_ops;
50162306a36Sopenharmony_ci		iio_trigger_set_drvdata(data->drdy_trig, indio_dev);
50262306a36Sopenharmony_ci		ret = iio_trigger_register(data->drdy_trig);
50362306a36Sopenharmony_ci		if (ret)
50462306a36Sopenharmony_ci			goto err_poweroff;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci		indio_dev->trig = iio_trigger_get(data->drdy_trig);
50762306a36Sopenharmony_ci	}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	ret = iio_triggered_buffer_setup(indio_dev, NULL,
51062306a36Sopenharmony_ci					 ccs811_trigger_handler, NULL);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	if (ret < 0) {
51362306a36Sopenharmony_ci		dev_err(&client->dev, "triggered buffer setup failed\n");
51462306a36Sopenharmony_ci		goto err_trigger_unregister;
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	ret = iio_device_register(indio_dev);
51862306a36Sopenharmony_ci	if (ret < 0) {
51962306a36Sopenharmony_ci		dev_err(&client->dev, "unable to register iio device\n");
52062306a36Sopenharmony_ci		goto err_buffer_cleanup;
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci	return 0;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cierr_buffer_cleanup:
52562306a36Sopenharmony_ci	iio_triggered_buffer_cleanup(indio_dev);
52662306a36Sopenharmony_cierr_trigger_unregister:
52762306a36Sopenharmony_ci	if (data->drdy_trig)
52862306a36Sopenharmony_ci		iio_trigger_unregister(data->drdy_trig);
52962306a36Sopenharmony_cierr_poweroff:
53062306a36Sopenharmony_ci	i2c_smbus_write_byte_data(client, CCS811_MEAS_MODE, CCS811_MODE_IDLE);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	return ret;
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic void ccs811_remove(struct i2c_client *client)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(client);
53862306a36Sopenharmony_ci	struct ccs811_data *data = iio_priv(indio_dev);
53962306a36Sopenharmony_ci	int ret;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	iio_device_unregister(indio_dev);
54262306a36Sopenharmony_ci	iio_triggered_buffer_cleanup(indio_dev);
54362306a36Sopenharmony_ci	if (data->drdy_trig)
54462306a36Sopenharmony_ci		iio_trigger_unregister(data->drdy_trig);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	ret = i2c_smbus_write_byte_data(client, CCS811_MEAS_MODE,
54762306a36Sopenharmony_ci					CCS811_MODE_IDLE);
54862306a36Sopenharmony_ci	if (ret)
54962306a36Sopenharmony_ci		dev_warn(&client->dev, "Failed to power down device (%pe)\n",
55062306a36Sopenharmony_ci			 ERR_PTR(ret));
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_cistatic const struct i2c_device_id ccs811_id[] = {
55462306a36Sopenharmony_ci	{"ccs811", 0},
55562306a36Sopenharmony_ci	{	}
55662306a36Sopenharmony_ci};
55762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ccs811_id);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic const struct of_device_id ccs811_dt_ids[] = {
56062306a36Sopenharmony_ci	{ .compatible = "ams,ccs811" },
56162306a36Sopenharmony_ci	{ }
56262306a36Sopenharmony_ci};
56362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ccs811_dt_ids);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_cistatic struct i2c_driver ccs811_driver = {
56662306a36Sopenharmony_ci	.driver = {
56762306a36Sopenharmony_ci		.name = "ccs811",
56862306a36Sopenharmony_ci		.of_match_table = ccs811_dt_ids,
56962306a36Sopenharmony_ci	},
57062306a36Sopenharmony_ci	.probe = ccs811_probe,
57162306a36Sopenharmony_ci	.remove = ccs811_remove,
57262306a36Sopenharmony_ci	.id_table = ccs811_id,
57362306a36Sopenharmony_ci};
57462306a36Sopenharmony_cimodule_i2c_driver(ccs811_driver);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ciMODULE_AUTHOR("Narcisa Vasile <narcisaanamaria12@gmail.com>");
57762306a36Sopenharmony_ciMODULE_DESCRIPTION("CCS811 volatile organic compounds sensor");
57862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
579