[PATCH v6 4/6] iio: pressure: dps310: add triggered buffer support
Rupesh Majhi <[email protected]>
| Newsgroups | org.kernel.vger.linux-iio,dev.linux.lists.llvm,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
Add a triggered buffer in order to capture continuously on both channels instead of one sysfs read at a time. Raw register value is not useful on its own, pressure has to go through the compensation polynomial and needs a temperature reading. Report raw in Pa with 1/1000 scale to keep full resolution in the buffer without changing what the existing processed attribute reports. Raw and processed reads return -EBUSY while buffer is on, so does any reconfiguration. Assisted-by: Claude:claude-opus-5 Signed-off-by: Rupesh Majhi <[email protected]> --- drivers/iio/pressure/Kconfig | 2 + drivers/iio/pressure/dps310.c | 169 ++++++++++++++++++++++++++++++++-- 2 files changed, 165 insertions(+), 6 deletions(-) diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index 838a8340c4c0..cef8b90b9ae7 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -112,6 +112,8 @@ config DPS310 tristate "Infineon DPS310 pressure and temperature sensor" depends on I2C select REGMAP_I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER help Support for the Infineon DPS310 digital barometric pressure sensor. It can be accessed over I2C bus. diff --git a/drivers/iio/pressure/dps310.c b/drivers/iio/pressure/dps310.c index 269a71ea3e7a..f122acd72b0c 100644 --- a/drivers/iio/pressure/dps310.c +++ b/drivers/iio/pressure/dps310.c @@ -21,8 +21,11 @@ #include <linux/regmap.h> #include <linux/unaligned.h> +#include <linux/iio/buffer.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> #define DPS310_DEV_NAME "dps310" @@ -93,19 +96,60 @@ struct dps310_data { bool timeout_recovery_failed; }; +enum dps310_scan_index { + DPS310_SCAN_TEMP, + DPS310_SCAN_PRESSURE, +}; + +struct dps310_scan { + s32 channels[2]; + aligned_s64 ts; +}; + static const struct iio_chan_spec dps310_channels[] = { { .type = IIO_TEMP, .info_mask_separate = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | BIT(IIO_CHAN_INFO_SAMP_FREQ) | BIT(IIO_CHAN_INFO_PROCESSED), + .scan_index = DPS310_SCAN_TEMP, + .scan_type = { + .sign = 's', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_CPU, + }, }, { .type = IIO_PRESSURE, + /* + * Pressure is only meaningful once the raw register value has + * been run through the compensation polynomial in section 4.9.1 + * of the datasheet, which needs a temperature reading as well. + * So what is reported as _raw here is already compensated, in + * Pa, and _scale converts it to the kPa the ABI asks for. The + * _processed attribute reports the same value and predates + * buffer support, so it has to stay. + * + * Do not copy this pattern into other drivers. A raw attribute + * that is not the raw register value is only tolerable here + * because the alternative is either losing resolution in the + * buffer or breaking existing users of _processed. + */ .info_mask_separate = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | BIT(IIO_CHAN_INFO_SAMP_FREQ) | - BIT(IIO_CHAN_INFO_PROCESSED), + BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .scan_index = DPS310_SCAN_PRESSURE, + .scan_type = { + .sign = 's', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_CPU, + }, }, + IIO_CHAN_SOFT_TIMESTAMP(2), }; /* To be called after checking the COEF_RDY bit in MEAS_CFG */ @@ -589,6 +633,11 @@ static int dps310_write_raw(struct iio_dev *iio, { struct dps310_data *data = iio_priv(iio); + /* Reconfiguring mid-capture would change the values being captured */ + IIO_DEV_ACQUIRE_DIRECT_MODE(iio, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + ACQUIRE(mutex_intr, lock)(&data->lock); if (ACQUIRE_ERR(mutex_intr, &lock)) return -EINTR; @@ -730,6 +779,13 @@ static int dps310_read_pressure(struct dps310_data *data, int *val, int *val2, return IIO_VAL_INT; + case IIO_CHAN_INFO_RAW: + rc = dps310_read_pressure_value(data, val); + if (rc) + return rc; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_PROCESSED: rc = dps310_read_pressure_value(data, val); if (rc) @@ -738,6 +794,12 @@ static int dps310_read_pressure(struct dps310_data *data, int *val, int *val2, *val2 = 1000; /* Convert Pa to KPa per IIO ABI */ return IIO_VAL_FRACTIONAL; + case IIO_CHAN_INFO_SCALE: + /* The raw value is in Pa, the ABI wants kPa */ + *val = 1; + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: rc = dps310_get_pres_precision(data, val); if (rc) @@ -819,12 +881,10 @@ static int dps310_read_temp(struct dps310_data *data, int *val, int *val2, } } -static int dps310_read_raw(struct iio_dev *iio, - struct iio_chan_spec const *chan, - int *val, int *val2, long mask) +static int dps310_read_channel(struct dps310_data *data, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { - struct dps310_data *data = iio_priv(iio); - switch (chan->type) { case IIO_PRESSURE: return dps310_read_pressure(data, val, val2, mask); @@ -837,6 +897,92 @@ static int dps310_read_raw(struct iio_dev *iio, } } +static int dps310_read_raw(struct iio_dev *iio, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct dps310_data *data = iio_priv(iio); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_PROCESSED: { + /* + * Sampling here consumes the same measurement the capture path + * reads, so refuse while the buffer is enabled. + */ + IIO_DEV_ACQUIRE_DIRECT_MODE(iio, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + return dps310_read_channel(data, chan, val, val2, mask); + } + default: + return dps310_read_channel(data, chan, val, val2, mask); + } +} + +static int dps310_fill_scan(struct dps310_data *data, + const unsigned long *scan_mask, + struct dps310_scan *scan) + __must_hold(&data->lock) +{ + int rc; + int i = 0; + + /* + * The pressure compensation needs a temperature reading, so temperature + * is sampled even when only the pressure channel is enabled. + */ + rc = dps310_read_temp_raw_locked(data); + if (rc) + return rc; + + if (test_bit(DPS310_SCAN_TEMP, scan_mask)) { + /* Millidegrees Celsius */ + rc = dps310_calculate_temp(data, &scan->channels[i]); + if (rc) + return rc; + + i++; + } + + if (test_bit(DPS310_SCAN_PRESSURE, scan_mask)) { + rc = dps310_read_pres_raw_locked(data); + if (rc) + return rc; + + /* Pascals, see the comment on the channel definition */ + rc = dps310_calculate_pressure(data, &scan->channels[i]); + if (rc) + return rc; + } + + return 0; +} + +static irqreturn_t dps310_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *iio = pf->indio_dev; + struct dps310_data *data = iio_priv(iio); + struct dps310_scan scan = { }; + int rc; + + mutex_lock(&data->lock); + rc = dps310_fill_scan(data, iio->active_scan_mask, &scan); + mutex_unlock(&data->lock); + if (rc) + goto err; + + iio_push_to_buffers_with_ts(iio, &scan, sizeof(scan), + iio_get_time_ns(iio)); + +err: + iio_trigger_notify_done(iio->trig); + + return IRQ_HANDLED; +} + static void dps310_reset(void *action_data) { struct dps310_data *data = action_data; @@ -893,6 +1039,17 @@ static int dps310_probe(struct i2c_client *client) if (rc) return rc; + /* + * The device measures continuously in background mode, so a capture is + * just a read of the latest results and no buffer setup ops are needed. + * The trigger is not aligned with the measurements either way, so the + * timestamp is taken in the handler rather than by a top half. + */ + rc = devm_iio_triggered_buffer_setup(dev, iio, NULL, + dps310_trigger_handler, NULL); + if (rc) + return rc; + rc = devm_iio_device_register(dev, iio); if (rc) return rc; -- 2.43.0