[PATCH 3/4] iio: light: ltr390: Interrupts and threshold event support

Abhash Jha posted 4 patches 2 months, 3 weeks ago
There is a newer version of this series
[PATCH 3/4] iio: light: ltr390: Interrupts and threshold event support
Posted by Abhash Jha 2 months, 3 weeks ago
Added support for threshold events for both the ALS and UVI channels.
The events are reported when the threshold interrupt is triggered. Both
rising and falling threshold types are supported.

Signed-off-by: Abhash Jha <abhashkumarjha123@gmail.com>
---
 drivers/iio/light/ltr390.c | 220 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 218 insertions(+), 2 deletions(-)

diff --git a/drivers/iio/light/ltr390.c b/drivers/iio/light/ltr390.c
index c4ff26d68..c707a4871 100644
--- a/drivers/iio/light/ltr390.c
+++ b/drivers/iio/light/ltr390.c
@@ -26,6 +26,7 @@
 #include <linux/bitfield.h>
 
 #include <linux/iio/iio.h>
+#include <linux/iio/events.h>
 
 #include <asm/unaligned.h>
 
@@ -33,9 +34,12 @@
 #define LTR390_ALS_UVS_MEAS_RATE	0x04
 #define LTR390_ALS_UVS_GAIN		0x05
 #define LTR390_PART_ID			0x06
+#define LTR390_MAIN_STATUS		0x07
 #define LTR390_ALS_DATA			0x0D
 #define LTR390_UVS_DATA			0x10
 #define LTR390_INT_CFG			0x19
+#define LTR390_THRESH_UP		0x21
+#define LTR390_THRESH_LOW		0x24
 
 #define LTR390_PART_NUMBER_ID		0xb
 #define LTR390_ALS_UVS_GAIN_MASK	0x07
@@ -46,6 +50,8 @@
 #define LTR390_SW_RESET	      BIT(4)
 #define LTR390_UVS_MODE	      BIT(3)
 #define LTR390_SENSOR_ENABLE  BIT(1)
+#define LTR390_LS_INT_EN      BIT(2)
+#define LTR390_LS_INT_SEL_UVS BIT(5)
 
 #define LTR390_FRACTIONAL_PRECISION 100
 
@@ -230,6 +236,22 @@ static const int ltr390_int_time_map_us[] = { 400000, 200000, 100000, 50000, 250
 static const int ltr390_gain_map[] = { 1, 3, 6, 9, 18 };
 static const int ltr390_freq_map[] = { 40000, 20000, 10000, 5000, 2000, 1000, 500, 500 };
 
+static const struct iio_event_spec ltr390_event_spec[] = {
+	{
+		.type = IIO_EV_TYPE_THRESH,
+		.dir = IIO_EV_DIR_RISING,
+		.mask_separate = BIT(IIO_EV_INFO_VALUE),
+	}, {
+		.type = IIO_EV_TYPE_THRESH,
+		.dir = IIO_EV_DIR_FALLING,
+		.mask_separate = BIT(IIO_EV_INFO_VALUE),
+	}, {
+		.type = IIO_EV_TYPE_THRESH,
+		.dir = IIO_EV_DIR_EITHER,
+		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
+	}
+};
+
 static const struct iio_chan_spec ltr390_channels[] = {
 	/* UV sensor */
 	{
@@ -238,7 +260,9 @@ static const struct iio_chan_spec ltr390_channels[] = {
 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
 		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
 		.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE)
-						| BIT(IIO_CHAN_INFO_SAMP_FREQ)
+						| BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.event_spec = ltr390_event_spec,
+		.num_event_specs = ARRAY_SIZE(ltr390_event_spec),
 	},
 	/* ALS sensor */
 	{
@@ -247,7 +271,9 @@ static const struct iio_chan_spec ltr390_channels[] = {
 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
 		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
 		.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE)
-						| BIT(IIO_CHAN_INFO_SAMP_FREQ)
+						| BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.event_spec = ltr390_event_spec,
+		.num_event_specs = ARRAY_SIZE(ltr390_event_spec),
 	},
 };
 
@@ -370,12 +396,186 @@ static int ltr390_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec cons
 	}
 }
 
+static int ltr390_read_threshold(const struct iio_dev *indio_dev,
+				enum iio_event_direction dir,
+				int *val, int *val2)
+{
+	struct ltr390_data *data = iio_priv(indio_dev);
+	int ret;
+
+	switch (dir) {
+	case IIO_EV_DIR_RISING:
+		ret = ltr390_register_read(data, LTR390_THRESH_UP);
+		if (ret < 0)
+			return ret;
+		*val = ret;
+		return IIO_VAL_INT;
+
+	case IIO_EV_DIR_FALLING:
+		ret = ltr390_register_read(data, LTR390_THRESH_LOW);
+		if (ret < 0)
+			return ret;
+		*val = ret;
+		return IIO_VAL_INT;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ltr390_write_threshold(struct iio_dev *indio_dev,
+				enum iio_event_direction dir,
+				int val, int val2)
+{
+	struct ltr390_data *data = iio_priv(indio_dev);
+	int ret;
+
+	guard(mutex)(&data->lock);
+	switch (dir) {
+	case IIO_EV_DIR_RISING:
+		ret = regmap_bulk_write(data->regmap,
+					LTR390_THRESH_UP,
+					&val, 3);
+		return ret;
+	case IIO_EV_DIR_FALLING:
+		ret = regmap_bulk_write(data->regmap,
+					LTR390_THRESH_LOW,
+					&val, 3);
+		return ret;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ltr390_read_event_value(struct iio_dev *indio_dev,
+				const struct iio_chan_spec *chan,
+				enum iio_event_type type,
+				enum iio_event_direction dir,
+				enum iio_event_info info,
+				int *val, int *val2)
+{
+	switch (info) {
+	case IIO_EV_INFO_VALUE:
+		return ltr390_read_threshold(indio_dev, dir, val, val2);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ltr390_write_event_value(struct iio_dev *indio_dev,
+				const struct iio_chan_spec *chan,
+				enum iio_event_type type,
+				enum iio_event_direction dir,
+				enum iio_event_info info,
+				int val, int val2)
+{
+	switch (info) {
+	case IIO_EV_INFO_VALUE:
+		if (val2 != 0)
+			return -EINVAL;
+
+		return ltr390_write_threshold(indio_dev, dir, val, val2);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ltr390_read_event_config(struct iio_dev *indio_dev,
+				const struct iio_chan_spec *chan,
+				enum iio_event_type type,
+				enum iio_event_direction dir)
+{
+	struct ltr390_data *data = iio_priv(indio_dev);
+	int ret, status;
+
+	ret = regmap_read(data->regmap, LTR390_INT_CFG, &status);
+	if (ret < 0)
+		return ret;
+
+	return status & LTR390_LS_INT_EN;
+}
+
+static int ltr390_write_event_config(struct iio_dev *indio_dev,
+				const struct iio_chan_spec *chan,
+				enum iio_event_type type,
+				enum iio_event_direction dir,
+				int state)
+{
+	struct ltr390_data *data = iio_priv(indio_dev);
+	int ret;
+
+	if (state != 1  && state != 0)
+		return -EINVAL;
+
+	if (state == 0)
+		return regmap_clear_bits(data->regmap, LTR390_INT_CFG, LTR390_LS_INT_EN);
+
+	guard(mutex)(&data->lock);
+	ret = regmap_set_bits(data->regmap, LTR390_INT_CFG, LTR390_LS_INT_EN);
+	if (ret < 0)
+		return ret;
+
+	switch (chan->type) {
+	case IIO_LIGHT:
+		ret = ltr390_set_mode(data, LTR390_SET_ALS_MODE);
+		if (ret < 0)
+			return ret;
+
+		return regmap_clear_bits(data->regmap, LTR390_INT_CFG, LTR390_LS_INT_SEL_UVS);
+
+	case IIO_UVINDEX:
+		ret = ltr390_set_mode(data, LTR390_SET_UVS_MODE);
+		if (ret < 0)
+			return ret;
+
+		return regmap_set_bits(data->regmap, LTR390_INT_CFG, LTR390_LS_INT_SEL_UVS);
+
+	default:
+		return -EINVAL;
+	}
+}
+
 static const struct iio_info ltr390_info = {
 	.read_raw = ltr390_read_raw,
 	.write_raw = ltr390_write_raw,
 	.read_avail = ltr390_read_avail,
+	.read_event_value = ltr390_read_event_value,
+	.read_event_config = ltr390_read_event_config,
+	.write_event_value = ltr390_write_event_value,
+	.write_event_config = ltr390_write_event_config,
 };
 
+static irqreturn_t ltr390_interrupt_handler(int irq, void *private)
+{
+	struct iio_dev *indio_dev = private;
+	struct ltr390_data *data = iio_priv(indio_dev);
+	int ret, status;
+
+	/* Reading the status register to clear the interrupt flag, Datasheet pg: 17*/
+	ret = regmap_read(data->regmap, LTR390_MAIN_STATUS, &status);
+	if (ret < 0)
+		return ret;
+
+	switch (data->mode) {
+	case LTR390_SET_ALS_MODE:
+		iio_push_event(indio_dev,
+			IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
+			IIO_EV_TYPE_THRESH,
+			IIO_EV_DIR_EITHER),
+			iio_get_time_ns(indio_dev));
+		break;
+
+	case LTR390_SET_UVS_MODE:
+		iio_push_event(indio_dev,
+			IIO_UNMOD_EVENT_CODE(IIO_UVINDEX, 0,
+			IIO_EV_TYPE_THRESH,
+			IIO_EV_DIR_EITHER),
+			iio_get_time_ns(indio_dev));
+		break;
+	}
+
+	return IRQ_HANDLED;
+}
+
 static int ltr390_probe(struct i2c_client *client)
 {
 	struct ltr390_data *data;
@@ -429,6 +629,22 @@ static int ltr390_probe(struct i2c_client *client)
 	if (ret)
 		return dev_err_probe(dev, ret, "failed to enable the sensor\n");
 
+	if (client->irq) {
+		int irq_flags = irq_get_trigger_type(client->irq);
+
+		if (!irq_flags)
+			irq_flags = IRQF_TRIGGER_FALLING;
+
+		ret = devm_request_threaded_irq(&client->dev, client->irq,
+						NULL, ltr390_interrupt_handler,
+						irq_flags | IRQF_ONESHOT,
+						"ltr390_thresh_event", indio_dev);
+		if (ret) {
+			dev_err(&client->dev, "request irq (%d) failed\n", client->irq);
+			return ret;
+		}
+	}
+
 	return devm_iio_device_register(dev, indio_dev);
 }
 
-- 
2.43.0
Re: [PATCH 3/4] iio: light: ltr390: Interrupts and threshold event support
Posted by kernel test robot 2 months, 3 weeks ago
Hi Abhash,

kernel test robot noticed the following build errors:

[auto build test ERROR on jic23-iio/togreg]
[also build test ERROR on next-20240909]
[cannot apply to linus/master v6.11-rc7]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Abhash-Jha/iio-light-ltr390-Suspend-and-Resume-support/20240909-193623
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
patch link:    https://lore.kernel.org/r/20240909103623.264113-4-abhashkumarjha123%40gmail.com
patch subject: [PATCH 3/4] iio: light: ltr390: Interrupts and threshold event support
config: i386-buildonly-randconfig-002-20240910 (https://download.01.org/0day-ci/archive/20240910/202409101339.74gDdc6n-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240910/202409101339.74gDdc6n-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202409101339.74gDdc6n-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/iio/light/ltr390.c: In function 'ltr390_probe':
>> drivers/iio/light/ltr390.c:633:33: error: implicit declaration of function 'irq_get_trigger_type' [-Werror=implicit-function-declaration]
     633 |                 int irq_flags = irq_get_trigger_type(client->irq);
         |                                 ^~~~~~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors


vim +/irq_get_trigger_type +633 drivers/iio/light/ltr390.c

   578	
   579	static int ltr390_probe(struct i2c_client *client)
   580	{
   581		struct ltr390_data *data;
   582		struct iio_dev *indio_dev;
   583		struct device *dev;
   584		int ret, part_number;
   585	
   586		dev = &client->dev;
   587		indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
   588		if (!indio_dev)
   589			return -ENOMEM;
   590	
   591		data = iio_priv(indio_dev);
   592	
   593		data->regmap = devm_regmap_init_i2c(client, &ltr390_regmap_config);
   594		if (IS_ERR(data->regmap))
   595			return dev_err_probe(dev, PTR_ERR(data->regmap),
   596					     "regmap initialization failed\n");
   597	
   598		data->client = client;
   599		/* default value of integration time from pg: 15 of the datasheet */
   600		data->int_time_us = 100000;
   601		/* default value of gain from pg: 16 of the datasheet */
   602		data->gain = 3;
   603		/* default mode for ltr390 is ALS mode */
   604		data->mode = LTR390_SET_ALS_MODE;
   605	
   606		mutex_init(&data->lock);
   607	
   608		indio_dev->info = &ltr390_info;
   609		indio_dev->channels = ltr390_channels;
   610		indio_dev->num_channels = ARRAY_SIZE(ltr390_channels);
   611		indio_dev->name = "ltr390";
   612	
   613		ret = regmap_read(data->regmap, LTR390_PART_ID, &part_number);
   614		if (ret)
   615			return dev_err_probe(dev, ret,
   616					     "failed to get sensor's part id\n");
   617		/* Lower 4 bits of `part_number` change with hardware revisions */
   618		if (part_number >> 4 != LTR390_PART_NUMBER_ID)
   619			dev_info(dev, "received invalid product id: 0x%x", part_number);
   620		dev_dbg(dev, "LTR390, product id: 0x%x\n", part_number);
   621	
   622		/* reset sensor, chip fails to respond to this, so ignore any errors */
   623		regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_SW_RESET);
   624	
   625		/* Wait for the registers to reset before proceeding */
   626		usleep_range(1000, 2000);
   627	
   628		ret = regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_SENSOR_ENABLE);
   629		if (ret)
   630			return dev_err_probe(dev, ret, "failed to enable the sensor\n");
   631	
   632		if (client->irq) {
 > 633			int irq_flags = irq_get_trigger_type(client->irq);
   634	
   635			if (!irq_flags)
   636				irq_flags = IRQF_TRIGGER_FALLING;
   637	
   638			ret = devm_request_threaded_irq(&client->dev, client->irq,
   639							NULL, ltr390_interrupt_handler,
   640							irq_flags | IRQF_ONESHOT,
   641							"ltr390_thresh_event", indio_dev);
   642			if (ret) {
   643				dev_err(&client->dev, "request irq (%d) failed\n", client->irq);
   644				return ret;
   645			}
   646		}
   647	
   648		return devm_iio_device_register(dev, indio_dev);
   649	}
   650	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH 3/4] iio: light: ltr390: Interrupts and threshold event support
Posted by kernel test robot 2 months, 3 weeks ago
Hi Abhash,

kernel test robot noticed the following build errors:

[auto build test ERROR on jic23-iio/togreg]
[also build test ERROR on next-20240909]
[cannot apply to linus/master v6.11-rc7]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Abhash-Jha/iio-light-ltr390-Suspend-and-Resume-support/20240909-193623
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
patch link:    https://lore.kernel.org/r/20240909103623.264113-4-abhashkumarjha123%40gmail.com
patch subject: [PATCH 3/4] iio: light: ltr390: Interrupts and threshold event support
config: x86_64-buildonly-randconfig-003-20240910 (https://download.01.org/0day-ci/archive/20240910/202409100717.2rKMS0oi-lkp@intel.com/config)
compiler: clang version 18.1.5 (https://github.com/llvm/llvm-project 617a15a9eac96088ae5e9134248d8236e34b91b1)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240910/202409100717.2rKMS0oi-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202409100717.2rKMS0oi-lkp@intel.com/

All errors (new ones prefixed by >>):

>> drivers/iio/light/ltr390.c:633:19: error: call to undeclared function 'irq_get_trigger_type'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
     633 |                 int irq_flags = irq_get_trigger_type(client->irq);
         |                                 ^
   1 error generated.


vim +/irq_get_trigger_type +633 drivers/iio/light/ltr390.c

   578	
   579	static int ltr390_probe(struct i2c_client *client)
   580	{
   581		struct ltr390_data *data;
   582		struct iio_dev *indio_dev;
   583		struct device *dev;
   584		int ret, part_number;
   585	
   586		dev = &client->dev;
   587		indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
   588		if (!indio_dev)
   589			return -ENOMEM;
   590	
   591		data = iio_priv(indio_dev);
   592	
   593		data->regmap = devm_regmap_init_i2c(client, &ltr390_regmap_config);
   594		if (IS_ERR(data->regmap))
   595			return dev_err_probe(dev, PTR_ERR(data->regmap),
   596					     "regmap initialization failed\n");
   597	
   598		data->client = client;
   599		/* default value of integration time from pg: 15 of the datasheet */
   600		data->int_time_us = 100000;
   601		/* default value of gain from pg: 16 of the datasheet */
   602		data->gain = 3;
   603		/* default mode for ltr390 is ALS mode */
   604		data->mode = LTR390_SET_ALS_MODE;
   605	
   606		mutex_init(&data->lock);
   607	
   608		indio_dev->info = &ltr390_info;
   609		indio_dev->channels = ltr390_channels;
   610		indio_dev->num_channels = ARRAY_SIZE(ltr390_channels);
   611		indio_dev->name = "ltr390";
   612	
   613		ret = regmap_read(data->regmap, LTR390_PART_ID, &part_number);
   614		if (ret)
   615			return dev_err_probe(dev, ret,
   616					     "failed to get sensor's part id\n");
   617		/* Lower 4 bits of `part_number` change with hardware revisions */
   618		if (part_number >> 4 != LTR390_PART_NUMBER_ID)
   619			dev_info(dev, "received invalid product id: 0x%x", part_number);
   620		dev_dbg(dev, "LTR390, product id: 0x%x\n", part_number);
   621	
   622		/* reset sensor, chip fails to respond to this, so ignore any errors */
   623		regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_SW_RESET);
   624	
   625		/* Wait for the registers to reset before proceeding */
   626		usleep_range(1000, 2000);
   627	
   628		ret = regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_SENSOR_ENABLE);
   629		if (ret)
   630			return dev_err_probe(dev, ret, "failed to enable the sensor\n");
   631	
   632		if (client->irq) {
 > 633			int irq_flags = irq_get_trigger_type(client->irq);
   634	
   635			if (!irq_flags)
   636				irq_flags = IRQF_TRIGGER_FALLING;
   637	
   638			ret = devm_request_threaded_irq(&client->dev, client->irq,
   639							NULL, ltr390_interrupt_handler,
   640							irq_flags | IRQF_ONESHOT,
   641							"ltr390_thresh_event", indio_dev);
   642			if (ret) {
   643				dev_err(&client->dev, "request irq (%d) failed\n", client->irq);
   644				return ret;
   645			}
   646		}
   647	
   648		return devm_iio_device_register(dev, indio_dev);
   649	}
   650	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki