iio: temperature: tmp006: add triggered buffer support

Add support for continuous data capture using triggered buffers for the
tmp006 sensor. The device features a "data ready" interrupt line which
is pulled down once a new measurement is ready to be read.

Signed-off-by: Antoni Pokusinski <apokusinski01@gmail.com>
Link: https://patch.msgid.link/20240908172153.177406-2-apokusinski01@gmail.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
This commit is contained in:
Antoni Pokusinski 2024-09-08 19:21:53 +02:00 committed by Jonathan Cameron
parent 819b69abb1
commit 91f75ccf9f
2 changed files with 123 additions and 13 deletions

View file

@ -91,6 +91,8 @@ config MLX90635
config TMP006 config TMP006
tristate "TMP006 infrared thermopile sensor" tristate "TMP006 infrared thermopile sensor"
depends on I2C depends on I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help help
If you say yes here you get support for the Texas Instruments If you say yes here you get support for the Texas Instruments
TMP006 infrared thermopile sensor. TMP006 infrared thermopile sensor.

View file

@ -7,8 +7,6 @@
* Driver for the Texas Instruments I2C 16-bit IR thermopile sensor * Driver for the Texas Instruments I2C 16-bit IR thermopile sensor
* *
* (7-bit I2C slave address 0x40, changeable via ADR pins) * (7-bit I2C slave address 0x40, changeable via ADR pins)
*
* TODO: data ready irq
*/ */
#include <linux/err.h> #include <linux/err.h>
@ -21,6 +19,9 @@
#include <linux/iio/iio.h> #include <linux/iio/iio.h>
#include <linux/iio/sysfs.h> #include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#define TMP006_VOBJECT 0x00 #define TMP006_VOBJECT 0x00
#define TMP006_TAMBIENT 0x01 #define TMP006_TAMBIENT 0x01
@ -45,6 +46,7 @@
struct tmp006_data { struct tmp006_data {
struct i2c_client *client; struct i2c_client *client;
u16 config; u16 config;
struct iio_trigger *drdy_trig;
}; };
static int tmp006_read_measurement(struct tmp006_data *data, u8 reg) static int tmp006_read_measurement(struct tmp006_data *data, u8 reg)
@ -83,15 +85,19 @@ static int tmp006_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_RAW: case IIO_CHAN_INFO_RAW:
if (channel->type == IIO_VOLTAGE) { if (channel->type == IIO_VOLTAGE) {
/* LSB is 156.25 nV */ /* LSB is 156.25 nV */
ret = tmp006_read_measurement(data, TMP006_VOBJECT); iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
if (ret < 0) ret = tmp006_read_measurement(data, TMP006_VOBJECT);
return ret; if (ret < 0)
return ret;
}
*val = sign_extend32(ret, 15); *val = sign_extend32(ret, 15);
} else if (channel->type == IIO_TEMP) { } else if (channel->type == IIO_TEMP) {
/* LSB is 0.03125 degrees Celsius */ /* LSB is 0.03125 degrees Celsius */
ret = tmp006_read_measurement(data, TMP006_TAMBIENT); iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
if (ret < 0) ret = tmp006_read_measurement(data, TMP006_TAMBIENT);
return ret; if (ret < 0)
return ret;
}
*val = sign_extend32(ret, 15) >> TMP006_TAMBIENT_SHIFT; *val = sign_extend32(ret, 15) >> TMP006_TAMBIENT_SHIFT;
} else { } else {
break; break;
@ -128,7 +134,7 @@ static int tmp006_write_raw(struct iio_dev *indio_dev,
long mask) long mask)
{ {
struct tmp006_data *data = iio_priv(indio_dev); struct tmp006_data *data = iio_priv(indio_dev);
int i; int ret, i;
if (mask != IIO_CHAN_INFO_SAMP_FREQ) if (mask != IIO_CHAN_INFO_SAMP_FREQ)
return -EINVAL; return -EINVAL;
@ -136,13 +142,19 @@ static int tmp006_write_raw(struct iio_dev *indio_dev,
for (i = 0; i < ARRAY_SIZE(tmp006_freqs); i++) for (i = 0; i < ARRAY_SIZE(tmp006_freqs); i++)
if ((val == tmp006_freqs[i][0]) && if ((val == tmp006_freqs[i][0]) &&
(val2 == tmp006_freqs[i][1])) { (val2 == tmp006_freqs[i][1])) {
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
data->config &= ~TMP006_CONFIG_CR_MASK; data->config &= ~TMP006_CONFIG_CR_MASK;
data->config |= i << TMP006_CONFIG_CR_SHIFT; data->config |= i << TMP006_CONFIG_CR_SHIFT;
return i2c_smbus_write_word_swapped(data->client, ret = i2c_smbus_write_word_swapped(data->client,
TMP006_CONFIG, TMP006_CONFIG,
data->config); data->config);
iio_device_release_direct_mode(indio_dev);
return ret;
} }
return -EINVAL; return -EINVAL;
} }
@ -164,13 +176,29 @@ static const struct iio_chan_spec tmp006_channels[] = {
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE), BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
.scan_index = 0,
.scan_type = {
.sign = 's',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_BE,
}
}, },
{ {
.type = IIO_TEMP, .type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE), BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
} .scan_index = 1,
.scan_type = {
.sign = 's',
.realbits = 14,
.storagebits = 16,
.shift = TMP006_TAMBIENT_SHIFT,
.endianness = IIO_BE,
}
},
IIO_CHAN_SOFT_TIMESTAMP(2),
}; };
static const struct iio_info tmp006_info = { static const struct iio_info tmp006_info = {
@ -213,6 +241,54 @@ static void tmp006_powerdown_cleanup(void *dev)
tmp006_power(dev, false); tmp006_power(dev, false);
} }
static irqreturn_t tmp006_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct tmp006_data *data = iio_priv(indio_dev);
struct {
s16 channels[2];
s64 ts __aligned(8);
} scan;
s32 ret;
ret = i2c_smbus_read_word_data(data->client, TMP006_VOBJECT);
if (ret < 0)
goto err;
scan.channels[0] = ret;
ret = i2c_smbus_read_word_data(data->client, TMP006_TAMBIENT);
if (ret < 0)
goto err;
scan.channels[1] = ret;
iio_push_to_buffers_with_timestamp(indio_dev, &scan,
iio_get_time_ns(indio_dev));
err:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static int tmp006_set_trigger_state(struct iio_trigger *trig, bool state)
{
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
struct tmp006_data *data = iio_priv(indio_dev);
if (state)
data->config |= TMP006_CONFIG_DRDY_EN;
else
data->config &= ~TMP006_CONFIG_DRDY_EN;
return i2c_smbus_write_word_swapped(data->client, TMP006_CONFIG,
data->config);
}
static const struct iio_trigger_ops tmp006_trigger_ops = {
.set_trigger_state = tmp006_set_trigger_state,
};
static const unsigned long tmp006_scan_masks[] = { 0x3, 0 };
static int tmp006_probe(struct i2c_client *client) static int tmp006_probe(struct i2c_client *client)
{ {
struct iio_dev *indio_dev; struct iio_dev *indio_dev;
@ -241,6 +317,7 @@ static int tmp006_probe(struct i2c_client *client)
indio_dev->channels = tmp006_channels; indio_dev->channels = tmp006_channels;
indio_dev->num_channels = ARRAY_SIZE(tmp006_channels); indio_dev->num_channels = ARRAY_SIZE(tmp006_channels);
indio_dev->available_scan_masks = tmp006_scan_masks;
ret = i2c_smbus_read_word_swapped(data->client, TMP006_CONFIG); ret = i2c_smbus_read_word_swapped(data->client, TMP006_CONFIG);
if (ret < 0) if (ret < 0)
@ -258,6 +335,37 @@ static int tmp006_probe(struct i2c_client *client)
if (ret < 0) if (ret < 0)
return ret; return ret;
if (client->irq > 0) {
data->drdy_trig = devm_iio_trigger_alloc(&client->dev,
"%s-dev%d",
indio_dev->name,
iio_device_id(indio_dev));
if (!data->drdy_trig)
return -ENOMEM;
data->drdy_trig->ops = &tmp006_trigger_ops;
iio_trigger_set_drvdata(data->drdy_trig, indio_dev);
ret = iio_trigger_register(data->drdy_trig);
if (ret)
return ret;
indio_dev->trig = iio_trigger_get(data->drdy_trig);
ret = devm_request_threaded_irq(&client->dev, client->irq,
iio_trigger_generic_data_rdy_poll,
NULL,
IRQF_ONESHOT,
"tmp006_irq",
data->drdy_trig);
if (ret < 0)
return ret;
}
ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL,
tmp006_trigger_handler, NULL);
if (ret < 0)
return ret;
return devm_iio_device_register(&client->dev, indio_dev); return devm_iio_device_register(&client->dev, indio_dev);
} }