Skip to content

Commit

Permalink
iio: temperature: tmp006: add triggered buffer support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Antoni Pokusinski authored and Jonathan Cameron committed Sep 30, 2024
1 parent 819b69a commit 91f75cc
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 13 deletions.
2 changes: 2 additions & 0 deletions drivers/iio/temperature/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ config MLX90635
config TMP006
tristate "TMP006 infrared thermopile sensor"
depends on I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
If you say yes here you get support for the Texas Instruments
TMP006 infrared thermopile sensor.
Expand Down
134 changes: 121 additions & 13 deletions drivers/iio/temperature/tmp006.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
* Driver for the Texas Instruments I2C 16-bit IR thermopile sensor
*
* (7-bit I2C slave address 0x40, changeable via ADR pins)
*
* TODO: data ready irq
*/

#include <linux/err.h>
Expand All @@ -21,6 +19,9 @@

#include <linux/iio/iio.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_TAMBIENT 0x01
Expand All @@ -45,6 +46,7 @@
struct tmp006_data {
struct i2c_client *client;
u16 config;
struct iio_trigger *drdy_trig;
};

static int tmp006_read_measurement(struct tmp006_data *data, u8 reg)
Expand Down Expand Up @@ -83,15 +85,19 @@ static int tmp006_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_RAW:
if (channel->type == IIO_VOLTAGE) {
/* LSB is 156.25 nV */
ret = tmp006_read_measurement(data, TMP006_VOBJECT);
if (ret < 0)
return ret;
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
ret = tmp006_read_measurement(data, TMP006_VOBJECT);
if (ret < 0)
return ret;
}
*val = sign_extend32(ret, 15);
} else if (channel->type == IIO_TEMP) {
/* LSB is 0.03125 degrees Celsius */
ret = tmp006_read_measurement(data, TMP006_TAMBIENT);
if (ret < 0)
return ret;
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
ret = tmp006_read_measurement(data, TMP006_TAMBIENT);
if (ret < 0)
return ret;
}
*val = sign_extend32(ret, 15) >> TMP006_TAMBIENT_SHIFT;
} else {
break;
Expand Down Expand Up @@ -128,21 +134,27 @@ static int tmp006_write_raw(struct iio_dev *indio_dev,
long mask)
{
struct tmp006_data *data = iio_priv(indio_dev);
int i;
int ret, i;

if (mask != IIO_CHAN_INFO_SAMP_FREQ)
return -EINVAL;

for (i = 0; i < ARRAY_SIZE(tmp006_freqs); i++)
if ((val == tmp006_freqs[i][0]) &&
(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 |= i << TMP006_CONFIG_CR_SHIFT;

return i2c_smbus_write_word_swapped(data->client,
TMP006_CONFIG,
data->config);
ret = i2c_smbus_write_word_swapped(data->client,
TMP006_CONFIG,
data->config);

iio_device_release_direct_mode(indio_dev);
return ret;
}
return -EINVAL;
}
Expand All @@ -164,13 +176,29 @@ static const struct iio_chan_spec tmp006_channels[] = {
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
.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,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
.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 = {
Expand Down Expand Up @@ -213,6 +241,54 @@ static void tmp006_powerdown_cleanup(void *dev)
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)
{
struct iio_dev *indio_dev;
Expand Down Expand Up @@ -241,6 +317,7 @@ static int tmp006_probe(struct i2c_client *client)

indio_dev->channels = 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);
if (ret < 0)
Expand All @@ -258,6 +335,37 @@ static int tmp006_probe(struct i2c_client *client)
if (ret < 0)
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);
}

Expand Down

0 comments on commit 91f75cc

Please sign in to comment.