mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 00:39:03 -04:00
Replace the #include of <linux/mod_devicetable.h> by the more specific <linux/device-id/*.h> where applicable. For most cases the include can be dropped completely, only a few drivers need one or two headers added. Acked-by: Danilo Krummrich <dakr@kernel.org> Acked-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Acked-by: Bjorn Helgaas <bhelgaas@google.com> Link: https://patch.msgid.link/1a3f2007c5c5dcf555c09a4035ce3ae8ef1b6c49.1782808461.git.u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König (The Capable Hub) <u.kleine-koenig@baylibre.com>
225 lines
5.3 KiB
C
225 lines
5.3 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (c) 2025 Akhilesh Patil <akhilesh@ee.iitb.ac.in>
|
|
*
|
|
* Driver for adp810 pressure and temperature sensor
|
|
* Datasheet:
|
|
* https://aosong.com/userfiles/files/media/Datasheet%20ADP810-Digital.pdf
|
|
*/
|
|
|
|
#include <linux/array_size.h>
|
|
#include <linux/cleanup.h>
|
|
#include <linux/crc8.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/device.h>
|
|
#include <linux/dev_printk.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/module.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/types.h>
|
|
#include <linux/unaligned.h>
|
|
|
|
#include <linux/iio/iio.h>
|
|
#include <linux/iio/types.h>
|
|
|
|
/*
|
|
* Refer section 5.4 checksum calculation from datasheet.
|
|
* This sensor uses CRC polynomial x^8 + x^5 + x^4 + 1 (0x31)
|
|
*/
|
|
#define ADP810_CRC8_POLYNOMIAL 0x31
|
|
|
|
DECLARE_CRC8_TABLE(crc_table);
|
|
|
|
/*
|
|
* Buffer declaration which holds 9 bytes of measurement data read
|
|
* from the sensor. Use __packed to avoid any paddings, as data sent
|
|
* from the sensor is strictly contiguous 9 bytes.
|
|
*/
|
|
struct adp810_read_buf {
|
|
__be16 dp;
|
|
u8 dp_crc;
|
|
__be16 tmp;
|
|
u8 tmp_crc;
|
|
__be16 sf;
|
|
u8 sf_crc;
|
|
} __packed;
|
|
|
|
struct adp810_data {
|
|
struct i2c_client *client;
|
|
/* Use lock to synchronize access to device during read sequence */
|
|
struct mutex lock;
|
|
};
|
|
|
|
static int adp810_measure(struct adp810_data *data, struct adp810_read_buf *buf)
|
|
{
|
|
struct i2c_client *client = data->client;
|
|
struct device *dev = &client->dev;
|
|
int ret;
|
|
u8 trig_cmd[2] = {0x37, 0x2d};
|
|
|
|
/* Send trigger command to the sensor for measurement */
|
|
ret = i2c_master_send(client, trig_cmd, sizeof(trig_cmd));
|
|
if (ret < 0) {
|
|
dev_err(dev, "Error sending trigger command\n");
|
|
return ret;
|
|
}
|
|
if (ret != sizeof(trig_cmd))
|
|
return -EIO;
|
|
|
|
/*
|
|
* Wait for the sensor to acquire data. As per datasheet section 5.3.1,
|
|
* at least 10ms delay before reading from the sensor is recommended.
|
|
* Here, we wait for 20ms to have some safe margin on the top
|
|
* of recommendation and to compensate for any possible variations.
|
|
*/
|
|
msleep(20);
|
|
|
|
/* Read sensor values */
|
|
ret = i2c_master_recv(client, (char *)buf, sizeof(*buf));
|
|
if (ret < 0) {
|
|
dev_err(dev, "Error reading from sensor\n");
|
|
return ret;
|
|
}
|
|
if (ret != sizeof(*buf))
|
|
return -EIO;
|
|
|
|
/* CRC checks */
|
|
crc8_populate_msb(crc_table, ADP810_CRC8_POLYNOMIAL);
|
|
if (buf->dp_crc != crc8(crc_table, (u8 *)&buf->dp, 0x2, CRC8_INIT_VALUE)) {
|
|
dev_err(dev, "CRC error for pressure\n");
|
|
return -EIO;
|
|
}
|
|
|
|
if (buf->tmp_crc != crc8(crc_table, (u8 *)&buf->tmp, 0x2, CRC8_INIT_VALUE)) {
|
|
dev_err(dev, "CRC error for temperature\n");
|
|
return -EIO;
|
|
}
|
|
|
|
if (buf->sf_crc != crc8(crc_table, (u8 *)&buf->sf, 0x2, CRC8_INIT_VALUE)) {
|
|
dev_err(dev, "CRC error for scale\n");
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int adp810_read_raw(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan,
|
|
int *val, int *val2, long mask)
|
|
{
|
|
struct adp810_data *data = iio_priv(indio_dev);
|
|
struct device *dev = &data->client->dev;
|
|
struct adp810_read_buf buf = { };
|
|
int ret;
|
|
|
|
scoped_guard(mutex, &data->lock) {
|
|
ret = adp810_measure(data, &buf);
|
|
if (ret) {
|
|
dev_err(dev, "Failed to read from device\n");
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_RAW:
|
|
switch (chan->type) {
|
|
case IIO_PRESSURE:
|
|
*val = get_unaligned_be16(&buf.dp);
|
|
return IIO_VAL_INT;
|
|
case IIO_TEMP:
|
|
*val = get_unaligned_be16(&buf.tmp);
|
|
return IIO_VAL_INT;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
case IIO_CHAN_INFO_SCALE:
|
|
switch (chan->type) {
|
|
case IIO_PRESSURE:
|
|
*val = get_unaligned_be16(&buf.sf);
|
|
return IIO_VAL_INT;
|
|
case IIO_TEMP:
|
|
*val = 200;
|
|
return IIO_VAL_INT;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static const struct iio_info adp810_info = {
|
|
.read_raw = adp810_read_raw,
|
|
};
|
|
|
|
static const struct iio_chan_spec adp810_channels[] = {
|
|
{
|
|
.type = IIO_PRESSURE,
|
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
|
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
|
},
|
|
{
|
|
.type = IIO_TEMP,
|
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
|
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
|
},
|
|
};
|
|
|
|
static int adp810_probe(struct i2c_client *client)
|
|
{
|
|
struct device *dev = &client->dev;
|
|
struct iio_dev *indio_dev;
|
|
struct adp810_data *data;
|
|
int ret;
|
|
|
|
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
|
if (!indio_dev)
|
|
return -ENOMEM;
|
|
|
|
data = iio_priv(indio_dev);
|
|
data->client = client;
|
|
|
|
ret = devm_mutex_init(dev, &data->lock);
|
|
if (ret)
|
|
return ret;
|
|
|
|
indio_dev->name = "adp810";
|
|
indio_dev->channels = adp810_channels;
|
|
indio_dev->num_channels = ARRAY_SIZE(adp810_channels);
|
|
indio_dev->info = &adp810_info;
|
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
|
|
|
ret = devm_iio_device_register(dev, indio_dev);
|
|
if (ret)
|
|
return dev_err_probe(dev, ret, "Failed to register IIO device\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct i2c_device_id adp810_id_table[] = {
|
|
{ .name = "adp810" },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(i2c, adp810_id_table);
|
|
|
|
static const struct of_device_id adp810_of_table[] = {
|
|
{ .compatible = "aosong,adp810" },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, adp810_of_table);
|
|
|
|
static struct i2c_driver adp810_driver = {
|
|
.driver = {
|
|
.name = "adp810",
|
|
.of_match_table = adp810_of_table,
|
|
},
|
|
.probe = adp810_probe,
|
|
.id_table = adp810_id_table,
|
|
};
|
|
module_i2c_driver(adp810_driver);
|
|
|
|
MODULE_AUTHOR("Akhilesh Patil <akhilesh@ee.iitb.ac.in>");
|
|
MODULE_DESCRIPTION("Driver for Aosong ADP810 sensor");
|
|
MODULE_LICENSE("GPL");
|