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