1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (c) 2025 Akhilesh Patil <akhilesh@ee.iitb.ac.in>
4 *
5 * Driver for adp810 pressure and temperature sensor
6 * Datasheet:
7 * https://aosong.com/userfiles/files/media/Datasheet%20ADP810-Digital.pdf
8 */
9
10 #include <linux/array_size.h>
11 #include <linux/cleanup.h>
12 #include <linux/crc8.h>
13 #include <linux/delay.h>
14 #include <linux/device.h>
15 #include <linux/dev_printk.h>
16 #include <linux/errno.h>
17 #include <linux/i2c.h>
18 #include <linux/module.h>
19 #include <linux/mutex.h>
20 #include <linux/types.h>
21 #include <linux/unaligned.h>
22
23 #include <linux/iio/iio.h>
24 #include <linux/iio/types.h>
25
26 /*
27 * Refer section 5.4 checksum calculation from datasheet.
28 * This sensor uses CRC polynomial x^8 + x^5 + x^4 + 1 (0x31)
29 */
30 #define ADP810_CRC8_POLYNOMIAL 0x31
31
32 DECLARE_CRC8_TABLE(crc_table);
33
34 /*
35 * Buffer declaration which holds 9 bytes of measurement data read
36 * from the sensor. Use __packed to avoid any paddings, as data sent
37 * from the sensor is strictly contiguous 9 bytes.
38 */
39 struct adp810_read_buf {
40 __be16 dp;
41 u8 dp_crc;
42 __be16 tmp;
43 u8 tmp_crc;
44 __be16 sf;
45 u8 sf_crc;
46 } __packed;
47
48 struct adp810_data {
49 struct i2c_client *client;
50 /* Use lock to synchronize access to device during read sequence */
51 struct mutex lock;
52 };
53
adp810_measure(struct adp810_data * data,struct adp810_read_buf * buf)54 static int adp810_measure(struct adp810_data *data, struct adp810_read_buf *buf)
55 {
56 struct i2c_client *client = data->client;
57 struct device *dev = &client->dev;
58 int ret;
59 u8 trig_cmd[2] = {0x37, 0x2d};
60
61 /* Send trigger command to the sensor for measurement */
62 ret = i2c_master_send(client, trig_cmd, sizeof(trig_cmd));
63 if (ret < 0) {
64 dev_err(dev, "Error sending trigger command\n");
65 return ret;
66 }
67 if (ret != sizeof(trig_cmd))
68 return -EIO;
69
70 /*
71 * Wait for the sensor to acquire data. As per datasheet section 5.3.1,
72 * at least 10ms delay before reading from the sensor is recommended.
73 * Here, we wait for 20ms to have some safe margin on the top
74 * of recommendation and to compensate for any possible variations.
75 */
76 msleep(20);
77
78 /* Read sensor values */
79 ret = i2c_master_recv(client, (char *)buf, sizeof(*buf));
80 if (ret < 0) {
81 dev_err(dev, "Error reading from sensor\n");
82 return ret;
83 }
84 if (ret != sizeof(*buf))
85 return -EIO;
86
87 /* CRC checks */
88 crc8_populate_msb(crc_table, ADP810_CRC8_POLYNOMIAL);
89 if (buf->dp_crc != crc8(crc_table, (u8 *)&buf->dp, 0x2, CRC8_INIT_VALUE)) {
90 dev_err(dev, "CRC error for pressure\n");
91 return -EIO;
92 }
93
94 if (buf->tmp_crc != crc8(crc_table, (u8 *)&buf->tmp, 0x2, CRC8_INIT_VALUE)) {
95 dev_err(dev, "CRC error for temperature\n");
96 return -EIO;
97 }
98
99 if (buf->sf_crc != crc8(crc_table, (u8 *)&buf->sf, 0x2, CRC8_INIT_VALUE)) {
100 dev_err(dev, "CRC error for scale\n");
101 return -EIO;
102 }
103
104 return 0;
105 }
106
adp810_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)107 static int adp810_read_raw(struct iio_dev *indio_dev,
108 struct iio_chan_spec const *chan,
109 int *val, int *val2, long mask)
110 {
111 struct adp810_data *data = iio_priv(indio_dev);
112 struct device *dev = &data->client->dev;
113 struct adp810_read_buf buf = { };
114 int ret;
115
116 scoped_guard(mutex, &data->lock) {
117 ret = adp810_measure(data, &buf);
118 if (ret) {
119 dev_err(dev, "Failed to read from device\n");
120 return ret;
121 }
122 }
123
124 switch (mask) {
125 case IIO_CHAN_INFO_RAW:
126 switch (chan->type) {
127 case IIO_PRESSURE:
128 *val = get_unaligned_be16(&buf.dp);
129 return IIO_VAL_INT;
130 case IIO_TEMP:
131 *val = get_unaligned_be16(&buf.tmp);
132 return IIO_VAL_INT;
133 default:
134 return -EINVAL;
135 }
136 case IIO_CHAN_INFO_SCALE:
137 switch (chan->type) {
138 case IIO_PRESSURE:
139 *val = get_unaligned_be16(&buf.sf);
140 return IIO_VAL_INT;
141 case IIO_TEMP:
142 *val = 200;
143 return IIO_VAL_INT;
144 default:
145 return -EINVAL;
146 }
147 default:
148 return -EINVAL;
149 }
150 }
151
152 static const struct iio_info adp810_info = {
153 .read_raw = adp810_read_raw,
154 };
155
156 static const struct iio_chan_spec adp810_channels[] = {
157 {
158 .type = IIO_PRESSURE,
159 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
160 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
161 },
162 {
163 .type = IIO_TEMP,
164 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
165 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
166 },
167 };
168
adp810_probe(struct i2c_client * client)169 static int adp810_probe(struct i2c_client *client)
170 {
171 struct device *dev = &client->dev;
172 struct iio_dev *indio_dev;
173 struct adp810_data *data;
174 int ret;
175
176 indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
177 if (!indio_dev)
178 return -ENOMEM;
179
180 data = iio_priv(indio_dev);
181 data->client = client;
182
183 ret = devm_mutex_init(dev, &data->lock);
184 if (ret)
185 return ret;
186
187 indio_dev->name = "adp810";
188 indio_dev->channels = adp810_channels;
189 indio_dev->num_channels = ARRAY_SIZE(adp810_channels);
190 indio_dev->info = &adp810_info;
191 indio_dev->modes = INDIO_DIRECT_MODE;
192
193 ret = devm_iio_device_register(dev, indio_dev);
194 if (ret)
195 return dev_err_probe(dev, ret, "Failed to register IIO device\n");
196
197 return 0;
198 }
199
200 static const struct i2c_device_id adp810_id_table[] = {
201 { .name = "adp810" },
202 { }
203 };
204 MODULE_DEVICE_TABLE(i2c, adp810_id_table);
205
206 static const struct of_device_id adp810_of_table[] = {
207 { .compatible = "aosong,adp810" },
208 { }
209 };
210 MODULE_DEVICE_TABLE(of, adp810_of_table);
211
212 static struct i2c_driver adp810_driver = {
213 .driver = {
214 .name = "adp810",
215 .of_match_table = adp810_of_table,
216 },
217 .probe = adp810_probe,
218 .id_table = adp810_id_table,
219 };
220 module_i2c_driver(adp810_driver);
221
222 MODULE_AUTHOR("Akhilesh Patil <akhilesh@ee.iitb.ac.in>");
223 MODULE_DESCRIPTION("Driver for Aosong ADP810 sensor");
224 MODULE_LICENSE("GPL");
225