File Index
Files and directories
Sign in
FILE

adp810.c

File preview with syntax highlighting.

linux-6.19.14/drivers/iio/pressure/adp810.c C
  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
 33DECLARE_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 */
 40struct 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
 49struct adp810_data {
 50	struct i2c_client *client;
 51	/* Use lock to synchronize access to device during read sequence */
 52	struct mutex lock;
 53};
 54
 55static 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
108static 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
153static const struct iio_info adp810_info = {
154	.read_raw = adp810_read_raw,
155};
156
157static 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
170static 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
201static const struct i2c_device_id adp810_id_table[] = {
202	{ "adp810" },
203	{ }
204};
205MODULE_DEVICE_TABLE(i2c, adp810_id_table);
206
207static const struct of_device_id adp810_of_table[] = {
208	{ .compatible = "aosong,adp810" },
209	{ }
210};
211MODULE_DEVICE_TABLE(of, adp810_of_table);
212
213static 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};
221module_i2c_driver(adp810_driver);
222
223MODULE_AUTHOR("Akhilesh Patil <akhilesh@ee.iitb.ac.in>");
224MODULE_DESCRIPTION("Driver for Aosong ADP810 sensor");
225MODULE_LICENSE("GPL");
~/ Read-only file preview.