File Index
Files and directories
Sign in
FILE

ags02ma.c

File preview with syntax highlighting.

linux-6.19.14/drivers/iio/chemical/ags02ma.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Copyright (C) 2023 Anshul Dalal <anshulusr@gmail.com>
  4 *
  5 * Driver for Aosong AGS02MA
  6 *
  7 * Datasheet:
  8 *   https://asairsensors.com/wp-content/uploads/2021/09/AGS02MA.pdf
  9 * Product Page:
 10 *   http://www.aosong.com/m/en/products-33.html
 11 */
 12
 13#include <linux/crc8.h>
 14#include <linux/delay.h>
 15#include <linux/i2c.h>
 16#include <linux/module.h>
 17
 18#include <linux/iio/iio.h>
 19
 20#define AGS02MA_TVOC_READ_REG		   0x00
 21#define AGS02MA_VERSION_REG		   0x11
 22
 23#define AGS02MA_VERSION_PROCESSING_DELAY   30
 24#define AGS02MA_TVOC_READ_PROCESSING_DELAY 1500
 25
 26#define AGS02MA_CRC8_INIT		   0xff
 27#define AGS02MA_CRC8_POLYNOMIAL		   0x31
 28
 29DECLARE_CRC8_TABLE(ags02ma_crc8_table);
 30
 31struct ags02ma_data {
 32	struct i2c_client *client;
 33};
 34
 35struct ags02ma_reading {
 36	__be32 data;
 37	u8 crc;
 38} __packed;
 39
 40static int ags02ma_register_read(struct i2c_client *client, u8 reg, u16 delay,
 41				 u32 *val)
 42{
 43	int ret;
 44	u8 crc;
 45	struct ags02ma_reading read_buffer;
 46
 47	ret = i2c_master_send(client, &reg, sizeof(reg));
 48	if (ret < 0) {
 49		dev_err(&client->dev,
 50			"Failed to send data to register 0x%x: %d", reg, ret);
 51		return ret;
 52	}
 53
 54	/* Processing Delay, Check Table 7.7 in the datasheet */
 55	msleep_interruptible(delay);
 56
 57	ret = i2c_master_recv(client, (u8 *)&read_buffer, sizeof(read_buffer));
 58	if (ret < 0) {
 59		dev_err(&client->dev,
 60			"Failed to receive from register 0x%x: %d", reg, ret);
 61		return ret;
 62	}
 63
 64	crc = crc8(ags02ma_crc8_table, (u8 *)&read_buffer.data,
 65		   sizeof(read_buffer.data), AGS02MA_CRC8_INIT);
 66	if (crc != read_buffer.crc) {
 67		dev_err(&client->dev, "CRC error\n");
 68		return -EIO;
 69	}
 70
 71	*val = be32_to_cpu(read_buffer.data);
 72	return 0;
 73}
 74
 75static int ags02ma_read_raw(struct iio_dev *iio_device,
 76			    struct iio_chan_spec const *chan, int *val,
 77			    int *val2, long mask)
 78{
 79	int ret;
 80	struct ags02ma_data *data = iio_priv(iio_device);
 81
 82	switch (mask) {
 83	case IIO_CHAN_INFO_RAW:
 84		ret = ags02ma_register_read(data->client, AGS02MA_TVOC_READ_REG,
 85					    AGS02MA_TVOC_READ_PROCESSING_DELAY,
 86					    val);
 87		if (ret < 0)
 88			return ret;
 89		return IIO_VAL_INT;
 90	case IIO_CHAN_INFO_SCALE:
 91		/* The sensor reads data as ppb */
 92		*val = 0;
 93		*val2 = 100;
 94		return IIO_VAL_INT_PLUS_NANO;
 95	default:
 96		return -EINVAL;
 97	}
 98}
 99
100static const struct iio_info ags02ma_info = {
101	.read_raw = ags02ma_read_raw,
102};
103
104static const struct iio_chan_spec ags02ma_channel = {
105	.type = IIO_CONCENTRATION,
106	.channel2 = IIO_MOD_VOC,
107	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
108		BIT(IIO_CHAN_INFO_SCALE),
109};
110
111static int ags02ma_probe(struct i2c_client *client)
112{
113	int ret;
114	struct ags02ma_data *data;
115	struct iio_dev *indio_dev;
116	u32 version;
117
118	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
119	if (!indio_dev)
120		return -ENOMEM;
121
122	crc8_populate_msb(ags02ma_crc8_table, AGS02MA_CRC8_POLYNOMIAL);
123
124	ret = ags02ma_register_read(client, AGS02MA_VERSION_REG,
125				    AGS02MA_VERSION_PROCESSING_DELAY, &version);
126	if (ret < 0)
127		return dev_err_probe(&client->dev, ret,
128			      "Failed to read device version\n");
129	dev_dbg(&client->dev, "Aosong AGS02MA, Version: 0x%x", version);
130
131	data = iio_priv(indio_dev);
132	data->client = client;
133	indio_dev->info = &ags02ma_info;
134	indio_dev->channels = &ags02ma_channel;
135	indio_dev->num_channels = 1;
136	indio_dev->name = "ags02ma";
137
138	return devm_iio_device_register(&client->dev, indio_dev);
139}
140
141static const struct i2c_device_id ags02ma_id_table[] = {
142	{ "ags02ma" },
143	{ }
144};
145MODULE_DEVICE_TABLE(i2c, ags02ma_id_table);
146
147static const struct of_device_id ags02ma_of_table[] = {
148	{ .compatible = "aosong,ags02ma" },
149	{ }
150};
151MODULE_DEVICE_TABLE(of, ags02ma_of_table);
152
153static struct i2c_driver ags02ma_driver = {
154	.driver = {
155		.name = "ags02ma",
156		.of_match_table = ags02ma_of_table,
157	},
158	.id_table = ags02ma_id_table,
159	.probe = ags02ma_probe,
160};
161module_i2c_driver(ags02ma_driver);
162
163MODULE_AUTHOR("Anshul Dalal <anshulusr@gmail.com>");
164MODULE_DESCRIPTION("Aosong AGS02MA TVOC Driver");
165MODULE_LICENSE("GPL");
~/ Read-only file preview.