]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
iio: mpl3115: add support for DRDY interrupt
authorAntoni Pokusinski <apokusinski01@gmail.com>
Thu, 2 Oct 2025 20:02:05 +0000 (22:02 +0200)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Sun, 19 Oct 2025 10:59:17 +0000 (11:59 +0100)
MPL3115 sensor features a "data ready" interrupt which indicates the
presence of new measurements.

Signed-off-by: Antoni Pokusinski <apokusinski01@gmail.com>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
drivers/iio/pressure/mpl3115.c

index 61830edd959b7002d4f5b4d9f7b609fa8f610f69..e1b2c9f2bb43d0b1da745662fdf8174fad2d71e1 100644 (file)
@@ -7,40 +7,66 @@
  * (7-bit I2C slave address 0x60)
  *
  * TODO: FIFO buffer, altimeter mode, oversampling, continuous mode,
- * interrupts, user offset correction, raw mode
+ * user offset correction, raw mode
  */
 
-#include <linux/module.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
 #include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/property.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/buffer.h>
 #include <linux/iio/triggered_buffer.h>
-#include <linux/delay.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/trigger.h>
 
 #define MPL3115_STATUS 0x00
 #define MPL3115_OUT_PRESS 0x01 /* MSB first, 20 bit */
 #define MPL3115_OUT_TEMP 0x04 /* MSB first, 12 bit */
 #define MPL3115_WHO_AM_I 0x0c
+#define MPL3115_INT_SOURCE 0x12
+#define MPL3115_PT_DATA_CFG 0x13
 #define MPL3115_CTRL_REG1 0x26
+#define MPL3115_CTRL_REG3 0x28
+#define MPL3115_CTRL_REG4 0x29
+#define MPL3115_CTRL_REG5 0x2a
 
 #define MPL3115_DEVICE_ID 0xc4
 
 #define MPL3115_STATUS_PRESS_RDY BIT(2)
 #define MPL3115_STATUS_TEMP_RDY BIT(1)
 
+#define MPL3115_INT_SRC_DRDY BIT(7)
+
+#define MPL3115_PT_DATA_EVENT_ALL GENMASK(2, 0)
+
 #define MPL3115_CTRL1_RESET BIT(2) /* software reset */
 #define MPL3115_CTRL1_OST BIT(1) /* initiate measurement */
 #define MPL3115_CTRL1_ACTIVE BIT(0) /* continuous measurement */
 #define MPL3115_CTRL1_OS_258MS GENMASK(5, 4) /* 64x oversampling */
 
+#define MPL3115_CTRL3_IPOL1 BIT(5)
+#define MPL3115_CTRL3_IPOL2 BIT(1)
+
+#define MPL3115_CTRL4_INT_EN_DRDY BIT(7)
+
+#define MPL3115_CTRL5_INT_CFG_DRDY BIT(7)
+
 struct mpl3115_data {
        struct i2c_client *client;
+       struct iio_trigger *drdy_trig;
        struct mutex lock;
        u8 ctrl_reg1;
 };
 
+enum mpl3115_irq_pin {
+       MPL3115_IRQ_INT1,
+       MPL3115_IRQ_INT2,
+};
+
 static int mpl3115_request(struct mpl3115_data *data)
 {
        int ret, tries = 15;
@@ -153,9 +179,11 @@ static int mpl3115_fill_trig_buffer(struct iio_dev *indio_dev, u8 *buffer)
        struct mpl3115_data *data = iio_priv(indio_dev);
        int ret, pos = 0;
 
-       ret = mpl3115_request(data);
-       if (ret < 0)
-               return ret;
+       if (!(data->ctrl_reg1 & MPL3115_CTRL1_ACTIVE)) {
+               ret = mpl3115_request(data);
+               if (ret < 0)
+                       return ret;
+       }
 
        if (test_bit(0, indio_dev->active_scan_mask)) {
                ret = i2c_smbus_read_i2c_block_data(data->client,
@@ -234,10 +262,145 @@ static const struct iio_chan_spec mpl3115_channels[] = {
        IIO_CHAN_SOFT_TIMESTAMP(2),
 };
 
+static irqreturn_t mpl3115_interrupt_handler(int irq, void *private)
+{
+       struct iio_dev *indio_dev = private;
+       struct mpl3115_data *data = iio_priv(indio_dev);
+       int ret;
+
+       ret = i2c_smbus_read_byte_data(data->client, MPL3115_INT_SOURCE);
+       if (ret < 0)
+               return IRQ_HANDLED;
+
+       if (!(ret & MPL3115_INT_SRC_DRDY))
+               return IRQ_NONE;
+
+       iio_trigger_poll_nested(data->drdy_trig);
+
+       return IRQ_HANDLED;
+}
+
+static int mpl3115_config_interrupt(struct mpl3115_data *data,
+                                   u8 ctrl_reg1, u8 ctrl_reg4)
+{
+       int ret;
+
+       ret = i2c_smbus_write_byte_data(data->client, MPL3115_CTRL_REG1,
+                                       ctrl_reg1);
+       if (ret < 0)
+               return ret;
+
+       ret = i2c_smbus_write_byte_data(data->client, MPL3115_CTRL_REG4,
+                                       ctrl_reg4);
+       if (ret < 0)
+               goto reg1_cleanup;
+
+       data->ctrl_reg1 = ctrl_reg1;
+
+       return 0;
+
+reg1_cleanup:
+       i2c_smbus_write_byte_data(data->client, MPL3115_CTRL_REG1,
+                                 data->ctrl_reg1);
+       return ret;
+}
+
+static int mpl3115_set_trigger_state(struct iio_trigger *trig, bool state)
+{
+       struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+       struct mpl3115_data *data = iio_priv(indio_dev);
+       u8 ctrl_reg1 = data->ctrl_reg1;
+       u8 ctrl_reg4 = state ? MPL3115_CTRL4_INT_EN_DRDY : 0;
+
+       if (state)
+               ctrl_reg1 |= MPL3115_CTRL1_ACTIVE;
+       else
+               ctrl_reg1 &= ~MPL3115_CTRL1_ACTIVE;
+
+       guard(mutex)(&data->lock);
+
+       return mpl3115_config_interrupt(data, ctrl_reg1, ctrl_reg4);
+}
+
+static const struct iio_trigger_ops mpl3115_trigger_ops = {
+       .set_trigger_state = mpl3115_set_trigger_state,
+};
+
 static const struct iio_info mpl3115_info = {
        .read_raw = &mpl3115_read_raw,
 };
 
+static int mpl3115_trigger_probe(struct mpl3115_data *data,
+                                struct iio_dev *indio_dev)
+{
+       struct fwnode_handle *fwnode = dev_fwnode(&data->client->dev);
+       int ret, irq, irq_type, irq_pin = MPL3115_IRQ_INT1;
+
+       irq = fwnode_irq_get_byname(fwnode, "INT1");
+       if (irq < 0) {
+               irq = fwnode_irq_get_byname(fwnode, "INT2");
+               if (irq < 0)
+                       return 0;
+
+               irq_pin = MPL3115_IRQ_INT2;
+       }
+
+       irq_type = irq_get_trigger_type(irq);
+       if (irq_type != IRQF_TRIGGER_RISING && irq_type != IRQF_TRIGGER_FALLING)
+               return -EINVAL;
+
+       ret = i2c_smbus_write_byte_data(data->client, MPL3115_PT_DATA_CFG,
+                                       MPL3115_PT_DATA_EVENT_ALL);
+       if (ret < 0)
+               return ret;
+
+       if (irq_pin == MPL3115_IRQ_INT1) {
+               ret = i2c_smbus_write_byte_data(data->client,
+                                               MPL3115_CTRL_REG5,
+                                               MPL3115_CTRL5_INT_CFG_DRDY);
+               if (ret)
+                       return ret;
+
+               if (irq_type == IRQF_TRIGGER_RISING) {
+                       ret = i2c_smbus_write_byte_data(data->client,
+                                                       MPL3115_CTRL_REG3,
+                                                       MPL3115_CTRL3_IPOL1);
+                       if (ret)
+                               return ret;
+               }
+       } else if (irq_type == IRQF_TRIGGER_RISING) {
+               ret = i2c_smbus_write_byte_data(data->client, MPL3115_CTRL_REG3,
+                                               MPL3115_CTRL3_IPOL2);
+               if (ret)
+                       return ret;
+       }
+
+       data->drdy_trig = devm_iio_trigger_alloc(&data->client->dev,
+                                                "%s-dev%d",
+                                                indio_dev->name,
+                                                iio_device_id(indio_dev));
+       if (!data->drdy_trig)
+               return -ENOMEM;
+
+       data->drdy_trig->ops = &mpl3115_trigger_ops;
+       iio_trigger_set_drvdata(data->drdy_trig, indio_dev);
+
+       ret = devm_request_threaded_irq(&data->client->dev, irq, NULL,
+                                       mpl3115_interrupt_handler,
+                                       IRQF_ONESHOT,
+                                       "mpl3115_irq", indio_dev);
+       if (ret)
+               return ret;
+
+       ret = devm_iio_trigger_register(&data->client->dev, data->drdy_trig);
+       if (ret)
+               return ret;
+
+       indio_dev->trig = iio_trigger_get(data->drdy_trig);
+
+       return 0;
+}
+
 static int mpl3115_probe(struct i2c_client *client)
 {
        const struct i2c_device_id *id = i2c_client_get_device_id(client);
@@ -277,6 +440,10 @@ static int mpl3115_probe(struct i2c_client *client)
        if (ret < 0)
                return ret;
 
+       ret = mpl3115_trigger_probe(data, indio_dev);
+       if (ret)
+               return ret;
+
        ret = iio_triggered_buffer_setup(indio_dev, NULL,
                mpl3115_trigger_handler, NULL);
        if (ret < 0)