brintos

brintos / linux-shallow public Read only

0
0
Text · 13.6 KiB · 17d1bc5 Raw
579 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * ccs811.c - Support for AMS CCS811 VOC Sensor4 *5 * Copyright (C) 2017 Narcisa Vasile <narcisaanamaria12@gmail.com>6 *7 * Datasheet: ams.com/content/download/951091/2269479/CCS811_DS000459_3-00.pdf8 *9 * IIO driver for AMS CCS811 (I2C address 0x5A/0x5B set by ADDR Low/High)10 *11 * TODO:12 * 1. Make the drive mode selectable form userspace13 * 2. Add support for interrupts14 * 3. Adjust time to wait for data to be ready based on selected operation mode15 * 4. Read error register and put the information in logs16 */17 18#include <linux/delay.h>19#include <linux/gpio/consumer.h>20#include <linux/i2c.h>21#include <linux/iio/iio.h>22#include <linux/iio/buffer.h>23#include <linux/iio/trigger.h>24#include <linux/iio/triggered_buffer.h>25#include <linux/iio/trigger_consumer.h>26#include <linux/module.h>27 28#define CCS811_STATUS		0x0029#define CCS811_MEAS_MODE	0x0130#define CCS811_ALG_RESULT_DATA	0x0231#define CCS811_RAW_DATA		0x0332#define CCS811_HW_ID		0x2033#define CCS811_HW_ID_VALUE	0x8134#define CCS811_HW_VERSION	0x2135#define CCS811_HW_VERSION_VALUE	0x1036#define CCS811_HW_VERSION_MASK	0xF037#define CCS811_ERR		0xE038/* Used to transition from boot to application mode */39#define CCS811_APP_START	0xF440#define CCS811_SW_RESET		0xFF41 42/* Status register flags */43#define CCS811_STATUS_ERROR		BIT(0)44#define CCS811_STATUS_DATA_READY	BIT(3)45#define CCS811_STATUS_APP_VALID_MASK	BIT(4)46#define CCS811_STATUS_APP_VALID_LOADED	BIT(4)47/*48 * Value of FW_MODE bit of STATUS register describes the sensor's state:49 * 0: Firmware is in boot mode, this allows new firmware to be loaded50 * 1: Firmware is in application mode. CCS811 is ready to take ADC measurements51 */52#define CCS811_STATUS_FW_MODE_MASK	BIT(7)53#define CCS811_STATUS_FW_MODE_APPLICATION	BIT(7)54 55/* Measurement modes */56#define CCS811_MODE_IDLE	0x0057#define CCS811_MODE_IAQ_1SEC	0x1058#define CCS811_MODE_IAQ_10SEC	0x2059#define CCS811_MODE_IAQ_60SEC	0x3060#define CCS811_MODE_RAW_DATA	0x4061 62#define CCS811_MEAS_MODE_INTERRUPT	BIT(3)63 64#define CCS811_VOLTAGE_MASK	0x3FF65 66struct ccs811_reading {67	__be16 co2;68	__be16 voc;69	u8 status;70	u8 error;71	__be16 raw_data;72} __attribute__((__packed__));73 74struct ccs811_data {75	struct i2c_client *client;76	struct mutex lock; /* Protect readings */77	struct ccs811_reading buffer;78	struct iio_trigger *drdy_trig;79	struct gpio_desc *wakeup_gpio;80	bool drdy_trig_on;81	/* Ensures correct alignment of timestamp if present */82	struct {83		s16 channels[2];84		s64 ts __aligned(8);85	} scan;86};87 88static const struct iio_chan_spec ccs811_channels[] = {89	{90		.type = IIO_CURRENT,91		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |92				      BIT(IIO_CHAN_INFO_SCALE),93		.scan_index = -1,94	}, {95		.type = IIO_VOLTAGE,96		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |97				      BIT(IIO_CHAN_INFO_SCALE),98		.scan_index = -1,99	}, {100		.type = IIO_CONCENTRATION,101		.channel2 = IIO_MOD_CO2,102		.modified = 1,103		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |104				      BIT(IIO_CHAN_INFO_SCALE),105		.scan_index = 0,106		.scan_type = {107			.sign = 'u',108			.realbits = 16,109			.storagebits = 16,110			.endianness = IIO_BE,111		},112	}, {113		.type = IIO_CONCENTRATION,114		.channel2 = IIO_MOD_VOC,115		.modified = 1,116		.info_mask_separate =  BIT(IIO_CHAN_INFO_RAW) |117				       BIT(IIO_CHAN_INFO_SCALE),118		.scan_index = 1,119		.scan_type = {120			.sign = 'u',121			.realbits = 16,122			.storagebits = 16,123			.endianness = IIO_BE,124		},125	},126	IIO_CHAN_SOFT_TIMESTAMP(2),127};128 129/*130 * The CCS811 powers-up in boot mode. A setup write to CCS811_APP_START will131 * transition the sensor to application mode.132 */133static int ccs811_start_sensor_application(struct i2c_client *client)134{135	int ret;136 137	ret = i2c_smbus_read_byte_data(client, CCS811_STATUS);138	if (ret < 0)139		return ret;140 141	if ((ret & CCS811_STATUS_FW_MODE_APPLICATION))142		return 0;143 144	if ((ret & CCS811_STATUS_APP_VALID_MASK) !=145	    CCS811_STATUS_APP_VALID_LOADED)146		return -EIO;147 148	ret = i2c_smbus_write_byte(client, CCS811_APP_START);149	if (ret < 0)150		return ret;151 152	ret = i2c_smbus_read_byte_data(client, CCS811_STATUS);153	if (ret < 0)154		return ret;155 156	if ((ret & CCS811_STATUS_FW_MODE_MASK) !=157	    CCS811_STATUS_FW_MODE_APPLICATION) {158		dev_err(&client->dev, "Application failed to start. Sensor is still in boot mode.\n");159		return -EIO;160	}161 162	return 0;163}164 165static int ccs811_setup(struct i2c_client *client)166{167	int ret;168 169	ret = ccs811_start_sensor_application(client);170	if (ret < 0)171		return ret;172 173	return i2c_smbus_write_byte_data(client, CCS811_MEAS_MODE,174					 CCS811_MODE_IAQ_1SEC);175}176 177static void ccs811_set_wakeup(struct ccs811_data *data, bool enable)178{179	if (!data->wakeup_gpio)180		return;181 182	gpiod_set_value(data->wakeup_gpio, enable);183 184	if (enable)185		usleep_range(50, 60);186	else187		usleep_range(20, 30);188}189 190static int ccs811_get_measurement(struct ccs811_data *data)191{192	int ret, tries = 11;193 194	ccs811_set_wakeup(data, true);195 196	/* Maximum waiting time: 1s, as measurements are made every second */197	while (tries-- > 0) {198		ret = i2c_smbus_read_byte_data(data->client, CCS811_STATUS);199		if (ret < 0)200			return ret;201 202		if ((ret & CCS811_STATUS_DATA_READY) || tries == 0)203			break;204		msleep(100);205	}206	if (!(ret & CCS811_STATUS_DATA_READY))207		return -EIO;208 209	ret = i2c_smbus_read_i2c_block_data(data->client,210					    CCS811_ALG_RESULT_DATA, 8,211					    (char *)&data->buffer);212	ccs811_set_wakeup(data, false);213 214	return ret;215}216 217static int ccs811_read_raw(struct iio_dev *indio_dev,218			   struct iio_chan_spec const *chan,219			   int *val, int *val2, long mask)220{221	struct ccs811_data *data = iio_priv(indio_dev);222	int ret;223 224	switch (mask) {225	case IIO_CHAN_INFO_RAW:226		ret = iio_device_claim_direct_mode(indio_dev);227		if (ret)228			return ret;229		mutex_lock(&data->lock);230		ret = ccs811_get_measurement(data);231		if (ret < 0) {232			mutex_unlock(&data->lock);233			iio_device_release_direct_mode(indio_dev);234			return ret;235		}236 237		switch (chan->type) {238		case IIO_VOLTAGE:239			*val = be16_to_cpu(data->buffer.raw_data) &240					   CCS811_VOLTAGE_MASK;241			ret = IIO_VAL_INT;242			break;243		case IIO_CURRENT:244			*val = be16_to_cpu(data->buffer.raw_data) >> 10;245			ret = IIO_VAL_INT;246			break;247		case IIO_CONCENTRATION:248			switch (chan->channel2) {249			case IIO_MOD_CO2:250				*val = be16_to_cpu(data->buffer.co2);251				ret =  IIO_VAL_INT;252				break;253			case IIO_MOD_VOC:254				*val = be16_to_cpu(data->buffer.voc);255				ret = IIO_VAL_INT;256				break;257			default:258				ret = -EINVAL;259			}260			break;261		default:262			ret = -EINVAL;263		}264		mutex_unlock(&data->lock);265		iio_device_release_direct_mode(indio_dev);266 267		return ret;268 269	case IIO_CHAN_INFO_SCALE:270		switch (chan->type) {271		case IIO_VOLTAGE:272			*val = 1;273			*val2 = 612903;274			return IIO_VAL_INT_PLUS_MICRO;275		case IIO_CURRENT:276			*val = 0;277			*val2 = 1000;278			return IIO_VAL_INT_PLUS_MICRO;279		case IIO_CONCENTRATION:280			switch (chan->channel2) {281			case IIO_MOD_CO2:282				*val = 0;283				*val2 = 100;284				return IIO_VAL_INT_PLUS_MICRO;285			case IIO_MOD_VOC:286				*val = 0;287				*val2 = 100;288				return IIO_VAL_INT_PLUS_NANO;289			default:290				return -EINVAL;291			}292		default:293			return -EINVAL;294		}295	default:296		return -EINVAL;297	}298}299 300static const struct iio_info ccs811_info = {301	.read_raw = ccs811_read_raw,302};303 304static int ccs811_set_trigger_state(struct iio_trigger *trig,305				    bool state)306{307	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);308	struct ccs811_data *data = iio_priv(indio_dev);309	int ret;310 311	ret = i2c_smbus_read_byte_data(data->client, CCS811_MEAS_MODE);312	if (ret < 0)313		return ret;314 315	if (state)316		ret |= CCS811_MEAS_MODE_INTERRUPT;317	else318		ret &= ~CCS811_MEAS_MODE_INTERRUPT;319 320	data->drdy_trig_on = state;321 322	return i2c_smbus_write_byte_data(data->client, CCS811_MEAS_MODE, ret);323}324 325static const struct iio_trigger_ops ccs811_trigger_ops = {326	.set_trigger_state = ccs811_set_trigger_state,327};328 329static irqreturn_t ccs811_trigger_handler(int irq, void *p)330{331	struct iio_poll_func *pf = p;332	struct iio_dev *indio_dev = pf->indio_dev;333	struct ccs811_data *data = iio_priv(indio_dev);334	struct i2c_client *client = data->client;335	int ret;336 337	ret = i2c_smbus_read_i2c_block_data(client, CCS811_ALG_RESULT_DATA,338					    sizeof(data->scan.channels),339					    (u8 *)data->scan.channels);340	if (ret != 4) {341		dev_err(&client->dev, "cannot read sensor data\n");342		goto err;343	}344 345	iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,346					   iio_get_time_ns(indio_dev));347 348err:349	iio_trigger_notify_done(indio_dev->trig);350 351	return IRQ_HANDLED;352}353 354static irqreturn_t ccs811_data_rdy_trigger_poll(int irq, void *private)355{356	struct iio_dev *indio_dev = private;357	struct ccs811_data *data = iio_priv(indio_dev);358 359	if (data->drdy_trig_on)360		iio_trigger_poll(data->drdy_trig);361 362	return IRQ_HANDLED;363}364 365static int ccs811_reset(struct i2c_client *client)366{367	struct gpio_desc *reset_gpio;368	int ret;369 370	reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",371					     GPIOD_OUT_LOW);372	if (IS_ERR(reset_gpio))373		return PTR_ERR(reset_gpio);374 375	/* Try to reset using nRESET pin if available else do SW reset */376	if (reset_gpio) {377		gpiod_set_value(reset_gpio, 1);378		usleep_range(20, 30);379		gpiod_set_value(reset_gpio, 0);380	} else {381		/*382		 * As per the datasheet, this sequence of values needs to be383		 * written to the SW_RESET register for triggering the soft384		 * reset in the device and placing it in boot mode.385		 */386		static const u8 reset_seq[] = {387			0x11, 0xE5, 0x72, 0x8A,388		};389 390		ret = i2c_smbus_write_i2c_block_data(client, CCS811_SW_RESET,391					     sizeof(reset_seq), reset_seq);392		if (ret < 0) {393			dev_err(&client->dev, "Failed to reset sensor\n");394			return ret;395		}396	}397 398	/* tSTART delay required after reset */399	usleep_range(1000, 2000);400 401	return 0;402}403 404static int ccs811_probe(struct i2c_client *client)405{406	const struct i2c_device_id *id = i2c_client_get_device_id(client);407	struct iio_dev *indio_dev;408	struct ccs811_data *data;409	int ret;410 411	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE412				     | I2C_FUNC_SMBUS_BYTE_DATA413				     | I2C_FUNC_SMBUS_READ_I2C_BLOCK))414		return -EOPNOTSUPP;415 416	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));417	if (!indio_dev)418		return -ENOMEM;419 420	data = iio_priv(indio_dev);421	i2c_set_clientdata(client, indio_dev);422	data->client = client;423 424	data->wakeup_gpio = devm_gpiod_get_optional(&client->dev, "wakeup",425						    GPIOD_OUT_HIGH);426	if (IS_ERR(data->wakeup_gpio))427		return PTR_ERR(data->wakeup_gpio);428 429	ccs811_set_wakeup(data, true);430 431	ret = ccs811_reset(client);432	if (ret) {433		ccs811_set_wakeup(data, false);434		return ret;435	}436 437	/* Check hardware id (should be 0x81 for this family of devices) */438	ret = i2c_smbus_read_byte_data(client, CCS811_HW_ID);439	if (ret < 0) {440		ccs811_set_wakeup(data, false);441		return ret;442	}443 444	if (ret != CCS811_HW_ID_VALUE) {445		dev_err(&client->dev, "hardware id doesn't match CCS81x\n");446		ccs811_set_wakeup(data, false);447		return -ENODEV;448	}449 450	ret = i2c_smbus_read_byte_data(client, CCS811_HW_VERSION);451	if (ret < 0) {452		ccs811_set_wakeup(data, false);453		return ret;454	}455 456	if ((ret & CCS811_HW_VERSION_MASK) != CCS811_HW_VERSION_VALUE) {457		dev_err(&client->dev, "no CCS811 sensor\n");458		ccs811_set_wakeup(data, false);459		return -ENODEV;460	}461 462	ret = ccs811_setup(client);463	if (ret < 0) {464		ccs811_set_wakeup(data, false);465		return ret;466	}467 468	ccs811_set_wakeup(data, false);469 470	mutex_init(&data->lock);471 472	indio_dev->name = id->name;473	indio_dev->info = &ccs811_info;474	indio_dev->modes = INDIO_DIRECT_MODE;475 476	indio_dev->channels = ccs811_channels;477	indio_dev->num_channels = ARRAY_SIZE(ccs811_channels);478 479	if (client->irq > 0) {480		ret = devm_request_threaded_irq(&client->dev, client->irq,481						ccs811_data_rdy_trigger_poll,482						NULL,483						IRQF_TRIGGER_FALLING |484						IRQF_ONESHOT,485						"ccs811_irq", indio_dev);486		if (ret) {487			dev_err(&client->dev, "irq request error %d\n", -ret);488			goto err_poweroff;489		}490 491		data->drdy_trig = devm_iio_trigger_alloc(&client->dev,492							 "%s-dev%d",493							 indio_dev->name,494							 iio_device_id(indio_dev));495		if (!data->drdy_trig) {496			ret = -ENOMEM;497			goto err_poweroff;498		}499 500		data->drdy_trig->ops = &ccs811_trigger_ops;501		iio_trigger_set_drvdata(data->drdy_trig, indio_dev);502		ret = iio_trigger_register(data->drdy_trig);503		if (ret)504			goto err_poweroff;505 506		indio_dev->trig = iio_trigger_get(data->drdy_trig);507	}508 509	ret = iio_triggered_buffer_setup(indio_dev, NULL,510					 ccs811_trigger_handler, NULL);511 512	if (ret < 0) {513		dev_err(&client->dev, "triggered buffer setup failed\n");514		goto err_trigger_unregister;515	}516 517	ret = iio_device_register(indio_dev);518	if (ret < 0) {519		dev_err(&client->dev, "unable to register iio device\n");520		goto err_buffer_cleanup;521	}522	return 0;523 524err_buffer_cleanup:525	iio_triggered_buffer_cleanup(indio_dev);526err_trigger_unregister:527	if (data->drdy_trig)528		iio_trigger_unregister(data->drdy_trig);529err_poweroff:530	i2c_smbus_write_byte_data(client, CCS811_MEAS_MODE, CCS811_MODE_IDLE);531 532	return ret;533}534 535static void ccs811_remove(struct i2c_client *client)536{537	struct iio_dev *indio_dev = i2c_get_clientdata(client);538	struct ccs811_data *data = iio_priv(indio_dev);539	int ret;540 541	iio_device_unregister(indio_dev);542	iio_triggered_buffer_cleanup(indio_dev);543	if (data->drdy_trig)544		iio_trigger_unregister(data->drdy_trig);545 546	ret = i2c_smbus_write_byte_data(client, CCS811_MEAS_MODE,547					CCS811_MODE_IDLE);548	if (ret)549		dev_warn(&client->dev, "Failed to power down device (%pe)\n",550			 ERR_PTR(ret));551}552 553static const struct i2c_device_id ccs811_id[] = {554	{ "ccs811" },555	{	}556};557MODULE_DEVICE_TABLE(i2c, ccs811_id);558 559static const struct of_device_id ccs811_dt_ids[] = {560	{ .compatible = "ams,ccs811" },561	{ }562};563MODULE_DEVICE_TABLE(of, ccs811_dt_ids);564 565static struct i2c_driver ccs811_driver = {566	.driver = {567		.name = "ccs811",568		.of_match_table = ccs811_dt_ids,569	},570	.probe = ccs811_probe,571	.remove = ccs811_remove,572	.id_table = ccs811_id,573};574module_i2c_driver(ccs811_driver);575 576MODULE_AUTHOR("Narcisa Vasile <narcisaanamaria12@gmail.com>");577MODULE_DESCRIPTION("CCS811 volatile organic compounds sensor");578MODULE_LICENSE("GPL v2");579