brintos

brintos / linux-shallow public Read only

0
0
Text · 4.2 KiB · 10156d7 Raw
190 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * ams-iaq-core.c - Support for AMS iAQ-Core VOC sensors4 *5 * Copyright (C) 2015, 20186 * Author: Matt Ranostay <matt.ranostay@konsulko.com>7 */8 9#include <linux/module.h>10#include <linux/mod_devicetable.h>11#include <linux/mutex.h>12#include <linux/init.h>13#include <linux/i2c.h>14#include <linux/iio/iio.h>15 16#define AMS_IAQCORE_DATA_SIZE		917 18#define AMS_IAQCORE_VOC_CO2_IDX		019#define AMS_IAQCORE_VOC_RESISTANCE_IDX	120#define AMS_IAQCORE_VOC_TVOC_IDX	221 22struct ams_iaqcore_reading {23	__be16 co2_ppm;24	u8 status;25	__be32 resistance;26	__be16 voc_ppb;27} __packed;28 29struct ams_iaqcore_data {30	struct i2c_client *client;31	struct mutex lock;32	unsigned long last_update;33 34	struct ams_iaqcore_reading buffer;35};36 37static const struct iio_chan_spec ams_iaqcore_channels[] = {38	{39		.type = IIO_CONCENTRATION,40		.channel2 = IIO_MOD_CO2,41		.modified = 1,42		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),43		.address = AMS_IAQCORE_VOC_CO2_IDX,44	},45	{46		.type = IIO_RESISTANCE,47		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),48		.address = AMS_IAQCORE_VOC_RESISTANCE_IDX,49	},50	{51		.type = IIO_CONCENTRATION,52		.channel2 = IIO_MOD_VOC,53		.modified = 1,54		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),55		.address = AMS_IAQCORE_VOC_TVOC_IDX,56	},57};58 59static int ams_iaqcore_read_measurement(struct ams_iaqcore_data *data)60{61	struct i2c_client *client = data->client;62	int ret;63 64	struct i2c_msg msg = {65		.addr = client->addr,66		.flags = client->flags | I2C_M_RD,67		.len = AMS_IAQCORE_DATA_SIZE,68		.buf = (char *) &data->buffer,69	};70 71	ret = i2c_transfer(client->adapter, &msg, 1);72 73	return (ret == AMS_IAQCORE_DATA_SIZE) ? 0 : ret;74}75 76static int ams_iaqcore_get_measurement(struct ams_iaqcore_data *data)77{78	int ret;79 80	/* sensor can only be polled once a second max per datasheet */81	if (!time_after(jiffies, data->last_update + HZ))82		return 0;83 84	ret = ams_iaqcore_read_measurement(data);85	if (ret < 0)86		return ret;87 88	data->last_update = jiffies;89 90	return 0;91}92 93static int ams_iaqcore_read_raw(struct iio_dev *indio_dev,94				struct iio_chan_spec const *chan, int *val,95				int *val2, long mask)96{97	struct ams_iaqcore_data *data = iio_priv(indio_dev);98	int ret;99 100	if (mask != IIO_CHAN_INFO_PROCESSED)101		return -EINVAL;102 103	mutex_lock(&data->lock);104	ret = ams_iaqcore_get_measurement(data);105 106	if (ret)107		goto err_out;108 109	switch (chan->address) {110	case AMS_IAQCORE_VOC_CO2_IDX:111		*val = 0;112		*val2 = be16_to_cpu(data->buffer.co2_ppm);113		ret = IIO_VAL_INT_PLUS_MICRO;114		break;115	case AMS_IAQCORE_VOC_RESISTANCE_IDX:116		*val = be32_to_cpu(data->buffer.resistance);117		ret = IIO_VAL_INT;118		break;119	case AMS_IAQCORE_VOC_TVOC_IDX:120		*val = 0;121		*val2 = be16_to_cpu(data->buffer.voc_ppb);122		ret = IIO_VAL_INT_PLUS_NANO;123		break;124	default:125		ret = -EINVAL;126	}127 128err_out:129	mutex_unlock(&data->lock);130 131	return ret;132}133 134static const struct iio_info ams_iaqcore_info = {135	.read_raw	= ams_iaqcore_read_raw,136};137 138static int ams_iaqcore_probe(struct i2c_client *client)139{140	struct iio_dev *indio_dev;141	struct ams_iaqcore_data *data;142 143	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));144	if (!indio_dev)145		return -ENOMEM;146 147	data = iio_priv(indio_dev);148	i2c_set_clientdata(client, indio_dev);149	data->client = client;150 151	/* so initial reading will complete */152	data->last_update = jiffies - HZ;153	mutex_init(&data->lock);154 155	indio_dev->info = &ams_iaqcore_info;156	indio_dev->name = dev_name(&client->dev);157	indio_dev->modes = INDIO_DIRECT_MODE;158 159	indio_dev->channels = ams_iaqcore_channels;160	indio_dev->num_channels = ARRAY_SIZE(ams_iaqcore_channels);161 162	return devm_iio_device_register(&client->dev, indio_dev);163}164 165static const struct i2c_device_id ams_iaqcore_id[] = {166	{ "ams-iaq-core" },167	{ }168};169MODULE_DEVICE_TABLE(i2c, ams_iaqcore_id);170 171static const struct of_device_id ams_iaqcore_dt_ids[] = {172	{ .compatible = "ams,iaq-core" },173	{ }174};175MODULE_DEVICE_TABLE(of, ams_iaqcore_dt_ids);176 177static struct i2c_driver ams_iaqcore_driver = {178	.driver = {179		.name	= "ams-iaq-core",180		.of_match_table = ams_iaqcore_dt_ids,181	},182	.probe = ams_iaqcore_probe,183	.id_table = ams_iaqcore_id,184};185module_i2c_driver(ams_iaqcore_driver);186 187MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");188MODULE_DESCRIPTION("AMS iAQ-Core VOC sensors");189MODULE_LICENSE("GPL v2");190