GNU Linux-libre 6.8.9-gnu
[releases.git] / drivers / iio / chemical / ags02ma.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
29 DECLARE_CRC8_TABLE(ags02ma_crc8_table);
30
31 struct ags02ma_data {
32         struct i2c_client *client;
33 };
34
35 struct ags02ma_reading {
36         __be32 data;
37         u8 crc;
38 } __packed;
39
40 static 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
75 static 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
100 static const struct iio_info ags02ma_info = {
101         .read_raw = ags02ma_read_raw,
102 };
103
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),
109 };
110
111 static 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
141 static const struct i2c_device_id ags02ma_id_table[] = {
142         { "ags02ma" },
143         { /* Sentinel */ }
144 };
145 MODULE_DEVICE_TABLE(i2c, ags02ma_id_table);
146
147 static const struct of_device_id ags02ma_of_table[] = {
148         { .compatible = "aosong,ags02ma" },
149         { /* Sentinel */ }
150 };
151 MODULE_DEVICE_TABLE(of, ags02ma_of_table);
152
153 static 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 };
161 module_i2c_driver(ags02ma_driver);
162
163 MODULE_AUTHOR("Anshul Dalal <anshulusr@gmail.com>");
164 MODULE_DESCRIPTION("Aosong AGS02MA TVOC Driver");
165 MODULE_LICENSE("GPL");