1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2023 Anshul Dalal <anshulusr@gmail.com>
5 * Driver for Aosong AGS02MA
8 * https://asairsensors.com/wp-content/uploads/2021/09/AGS02MA.pdf
10 * http://www.aosong.com/m/en/products-33.html
13 #include <linux/crc8.h>
14 #include <linux/delay.h>
15 #include <linux/i2c.h>
16 #include <linux/module.h>
18 #include <linux/iio/iio.h>
20 #define AGS02MA_TVOC_READ_REG 0x00
21 #define AGS02MA_VERSION_REG 0x11
23 #define AGS02MA_VERSION_PROCESSING_DELAY 30
24 #define AGS02MA_TVOC_READ_PROCESSING_DELAY 1500
26 #define AGS02MA_CRC8_INIT 0xff
27 #define AGS02MA_CRC8_POLYNOMIAL 0x31
29 DECLARE_CRC8_TABLE(ags02ma_crc8_table
);
32 struct i2c_client
*client
;
35 struct ags02ma_reading
{
40 static int ags02ma_register_read(struct i2c_client
*client
, u8 reg
, u16 delay
,
45 struct ags02ma_reading read_buffer
;
47 ret
= i2c_master_send(client
, ®
, sizeof(reg
));
50 "Failed to send data to register 0x%x: %d", reg
, ret
);
54 /* Processing Delay, Check Table 7.7 in the datasheet */
55 msleep_interruptible(delay
);
57 ret
= i2c_master_recv(client
, (u8
*)&read_buffer
, sizeof(read_buffer
));
60 "Failed to receive from register 0x%x: %d", reg
, ret
);
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");
71 *val
= be32_to_cpu(read_buffer
.data
);
75 static int ags02ma_read_raw(struct iio_dev
*iio_device
,
76 struct iio_chan_spec
const *chan
, int *val
,
80 struct ags02ma_data
*data
= iio_priv(iio_device
);
83 case IIO_CHAN_INFO_RAW
:
84 ret
= ags02ma_register_read(data
->client
, AGS02MA_TVOC_READ_REG
,
85 AGS02MA_TVOC_READ_PROCESSING_DELAY
,
90 case IIO_CHAN_INFO_SCALE
:
91 /* The sensor reads data as ppb */
94 return IIO_VAL_INT_PLUS_NANO
;
100 static const struct iio_info ags02ma_info
= {
101 .read_raw
= ags02ma_read_raw
,
104 static 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
),
111 static int ags02ma_probe(struct i2c_client
*client
)
114 struct ags02ma_data
*data
;
115 struct iio_dev
*indio_dev
;
118 indio_dev
= devm_iio_device_alloc(&client
->dev
, sizeof(*data
));
122 crc8_populate_msb(ags02ma_crc8_table
, AGS02MA_CRC8_POLYNOMIAL
);
124 ret
= ags02ma_register_read(client
, AGS02MA_VERSION_REG
,
125 AGS02MA_VERSION_PROCESSING_DELAY
, &version
);
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
);
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";
138 return devm_iio_device_register(&client
->dev
, indio_dev
);
141 static const struct i2c_device_id ags02ma_id_table
[] = {
145 MODULE_DEVICE_TABLE(i2c
, ags02ma_id_table
);
147 static const struct of_device_id ags02ma_of_table
[] = {
148 { .compatible
= "aosong,ags02ma" },
151 MODULE_DEVICE_TABLE(of
, ags02ma_of_table
);
153 static struct i2c_driver ags02ma_driver
= {
156 .of_match_table
= ags02ma_of_table
,
158 .id_table
= ags02ma_id_table
,
159 .probe
= ags02ma_probe
,
161 module_i2c_driver(ags02ma_driver
);
163 MODULE_AUTHOR("Anshul Dalal <anshulusr@gmail.com>");
164 MODULE_DESCRIPTION("Aosong AGS02MA TVOC Driver");
165 MODULE_LICENSE("GPL");