All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 0/9] iio: light: rpr0521
@ 2017-05-18 12:12 Mikko Koivunen
  2017-05-18 12:12 ` [PATCH v5 1/9] iio: light: rpr0521 disable sensor -bugfix Mikko Koivunen
                   ` (9 more replies)
  0 siblings, 10 replies; 22+ messages in thread
From: Mikko Koivunen @ 2017-05-18 12:12 UTC (permalink / raw)
  To: jic23; +Cc: pmeerw, knaack.h, lars, Daniel Baluta, linux-iio, Mikko Koivunen

Patchset v4->v5 changes:
  cover letter added
  0002 (void) removed

Patchset v3->v4 changes:
  0006 indent fix
  0009 on/off to preenable/postdisable

Patchset v2->v3 changes:
  More commits, numbers change, order change
  Changes listed in individual commits

Mikko Koivunen (9):
  iio: light: rpr0521 disable sensor -bugfix
  iio: light: rpr0521 poweroff for probe fails
  iio: light: rpr0521 on-off sequence change for CONFIG_PM
  iio: light: rpr0521 magic number to sizeof() on value read
  iio: light: rpr0521 whitespace fixes
  iio: light: rpr0521 sample_frequency read/write
  iio: light: rpr0521 proximity offset read/write
  iio: light: rpr0521 channel numbers reordered
  iio: light: rpr0521 triggered buffer

 drivers/iio/light/rpr0521.c |  597 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 549 insertions(+), 48 deletions(-)

-- 
1.7.9.5


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v5 1/9] iio: light: rpr0521 disable sensor -bugfix
  2017-05-18 12:12 [PATCH v5 0/9] iio: light: rpr0521 Mikko Koivunen
@ 2017-05-18 12:12 ` Mikko Koivunen
  2017-05-21 11:06   ` Jonathan Cameron
  2017-05-18 12:12 ` [PATCH v5 2/9] iio: light: rpr0521 poweroff for probe fails Mikko Koivunen
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 22+ messages in thread
From: Mikko Koivunen @ 2017-05-18 12:12 UTC (permalink / raw)
  To: jic23; +Cc: pmeerw, knaack.h, lars, Daniel Baluta, linux-iio, Mikko Koivunen

Sensor was marked enabled on each call even if the call was for disabling
sensor.

Signed-off-by: Mikko Koivunen <mikko.koivunen@fi.rohmeurope.com>
---
Tested on LeMaker HiKey with AOSP7.1 kernel 4.4.
Patch v2->v3 changes:
whitespace change.

 drivers/iio/light/rpr0521.c |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
index 7de0f39..03504f6 100644
--- a/drivers/iio/light/rpr0521.c
+++ b/drivers/iio/light/rpr0521.c
@@ -197,7 +197,10 @@ static int rpr0521_als_enable(struct rpr0521_data *data, u8 status)
 	if (ret < 0)
 		return ret;
 
-	data->als_dev_en = true;
+	if (status & RPR0521_MODE_ALS_MASK)
+		data->als_dev_en = true;
+	else
+		data->als_dev_en = false;
 
 	return 0;
 }
@@ -212,7 +215,10 @@ static int rpr0521_pxs_enable(struct rpr0521_data *data, u8 status)
 	if (ret < 0)
 		return ret;
 
-	data->pxs_dev_en = true;
+	if (status & RPR0521_MODE_PXS_MASK)
+		data->pxs_dev_en = true;
+	else
+		data->pxs_dev_en = false;
 
 	return 0;
 }
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v5 2/9] iio: light: rpr0521 poweroff for probe fails
  2017-05-18 12:12 [PATCH v5 0/9] iio: light: rpr0521 Mikko Koivunen
  2017-05-18 12:12 ` [PATCH v5 1/9] iio: light: rpr0521 disable sensor -bugfix Mikko Koivunen
@ 2017-05-18 12:12 ` Mikko Koivunen
  2017-05-21 11:08   ` Jonathan Cameron
  2017-05-18 12:12 ` [PATCH v5 3/9] iio: light: rpr0521 on-off sequence change for CONFIG_PM Mikko Koivunen
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 22+ messages in thread
From: Mikko Koivunen @ 2017-05-18 12:12 UTC (permalink / raw)
  To: jic23; +Cc: pmeerw, knaack.h, lars, Daniel Baluta, linux-iio, Mikko Koivunen

Set sensor measurement off after probe fail in pm_runtime_set_active() or
iio_device_register(). Without this change sensor measurement stays on
even though probe fails on these calls.

This is maybe rare case, but causes constant power drain without any
benefits when it happens. Power drain is 20-500uA, typically 180uA.

Signed-off-by: Mikko Koivunen <mikko.koivunen@fi.rohmeurope.com>
---
Sensor measurement is turned on rpr0521_init() so in probe it should be
turned off in error cases after that. With CONFIG_PM it's probably
unnecessary for iio_device_register()-fail, but without CONFIG_PM it is
needed. Writing power off twice when CONFIG_PM enabled is ok.

Patch v4->v5 changes:
errorpath (void) casting removed

 drivers/iio/light/rpr0521.c |   17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
index 03504f6..84d8d40 100644
--- a/drivers/iio/light/rpr0521.c
+++ b/drivers/iio/light/rpr0521.c
@@ -516,13 +516,26 @@ static int rpr0521_probe(struct i2c_client *client,
 
 	ret = pm_runtime_set_active(&client->dev);
 	if (ret < 0)
-		return ret;
+		goto err_poweroff;
 
 	pm_runtime_enable(&client->dev);
 	pm_runtime_set_autosuspend_delay(&client->dev, RPR0521_SLEEP_DELAY_MS);
 	pm_runtime_use_autosuspend(&client->dev);
 
-	return iio_device_register(indio_dev);
+	ret = iio_device_register(indio_dev);
+	if (ret)
+		goto err_pm_disable;
+
+	return 0;
+
+err_pm_disable:
+	pm_runtime_disable(&client->dev);
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_put_noidle(&client->dev);
+err_poweroff:
+	rpr0521_poweroff(data);
+
+	return ret;
 }
 
 static int rpr0521_remove(struct i2c_client *client)
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v5 3/9] iio: light: rpr0521 on-off sequence change for CONFIG_PM
  2017-05-18 12:12 [PATCH v5 0/9] iio: light: rpr0521 Mikko Koivunen
  2017-05-18 12:12 ` [PATCH v5 1/9] iio: light: rpr0521 disable sensor -bugfix Mikko Koivunen
  2017-05-18 12:12 ` [PATCH v5 2/9] iio: light: rpr0521 poweroff for probe fails Mikko Koivunen
@ 2017-05-18 12:12 ` Mikko Koivunen
  2017-05-21 11:10   ` Jonathan Cameron
  2017-05-18 12:12 ` [PATCH v5 4/9] iio: light: rpr0521 magic number to sizeof() on value read Mikko Koivunen
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 22+ messages in thread
From: Mikko Koivunen @ 2017-05-18 12:12 UTC (permalink / raw)
  To: jic23; +Cc: pmeerw, knaack.h, lars, Daniel Baluta, linux-iio, Mikko Koivunen

Refactor _set_power_state(), _resume() and _suspend().
Enable measurement only when needed, not in _init(). System can suspend
during measurement and measurement is continued on resume.
Pm turns off measurement when both ps and als measurements are disabled for
2 seconds. During off-time the power save is 20-500mA, typically 180mA.

Signed-off-by: Mikko Koivunen <mikko.koivunen@fi.rohmeurope.com>
---
 Don't enable measurement on _init() when using CONFIG_PM. Enable only
when needed, otherwise it messes up the pm suspend-resume. Polling
enables/disables the measurement anyway.

Save ALS/PS enabled status on _suspend() when called during measurement,
so that measurement can be re-enabled on _resume().

checkpatch.pl OK
Tested on LeMaker HiKey with AOSP7.1 kernel 4.4.
Builds ok with torvalds/linux feb 27.

 drivers/iio/light/rpr0521.c |   70 ++++++++++++++++++++++++++++---------------
 1 file changed, 46 insertions(+), 24 deletions(-)

diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
index 84d8d40..9219819 100644
--- a/drivers/iio/light/rpr0521.c
+++ b/drivers/iio/light/rpr0521.c
@@ -9,7 +9,7 @@
  *
  * IIO driver for RPR-0521RS (7-bit I2C slave address 0x38).
  *
- * TODO: illuminance channel, PM support, buffer
+ * TODO: illuminance channel, buffer
  */
 
 #include <linux/module.h>
@@ -142,9 +142,11 @@ struct rpr0521_data {
 	bool als_dev_en;
 	bool pxs_dev_en;
 
-	/* optimize runtime pm ops - enable device only if needed */
+	/* optimize runtime pm ops - enable/disable device only if needed */
 	bool als_ps_need_en;
 	bool pxs_ps_need_en;
+	bool als_need_dis;
+	bool pxs_need_dis;
 
 	struct regmap *regmap;
 };
@@ -230,40 +232,32 @@ static int rpr0521_pxs_enable(struct rpr0521_data *data, u8 status)
  * @on: state to be set for devices in @device_mask
  * @device_mask: bitmask specifying for which device we need to update @on state
  *
- * We rely on rpr0521_runtime_resume to enable our @device_mask devices, but
- * if (for example) PXS was enabled (pxs_dev_en = true) by a previous call to
- * rpr0521_runtime_resume and we want to enable ALS we MUST set ALS enable
- * bit of RPR0521_REG_MODE_CTRL here because rpr0521_runtime_resume will not
- * be called twice.
+ * Calls for this function must be balanced so that each ON should have matching
+ * OFF. Otherwise pm usage_count gets out of sync.
  */
 static int rpr0521_set_power_state(struct rpr0521_data *data, bool on,
 				   u8 device_mask)
 {
 #ifdef CONFIG_PM
 	int ret;
-	u8 update_mask = 0;
 
 	if (device_mask & RPR0521_MODE_ALS_MASK) {
-		if (on && !data->als_ps_need_en && data->pxs_dev_en)
-			update_mask |= RPR0521_MODE_ALS_MASK;
-		else
-			data->als_ps_need_en = on;
+		data->als_ps_need_en = on;
+		data->als_need_dis = !on;
 	}
 
 	if (device_mask & RPR0521_MODE_PXS_MASK) {
-		if (on && !data->pxs_ps_need_en && data->als_dev_en)
-			update_mask |= RPR0521_MODE_PXS_MASK;
-		else
-			data->pxs_ps_need_en = on;
-	}
-
-	if (update_mask) {
-		ret = regmap_update_bits(data->regmap, RPR0521_REG_MODE_CTRL,
-					 update_mask, update_mask);
-		if (ret < 0)
-			return ret;
+		data->pxs_ps_need_en = on;
+		data->pxs_need_dis = !on;
 	}
 
+	/*
+	 * On: _resume() is called only when we are suspended
+	 * Off: _suspend() is called after delay if _resume() is not
+	 * called before that.
+	 * Note: If either measurement is re-enabled before _suspend(),
+	 * both stay enabled until _suspend().
+	 */
 	if (on) {
 		ret = pm_runtime_get_sync(&data->client->dev);
 	} else {
@@ -279,6 +273,23 @@ static int rpr0521_set_power_state(struct rpr0521_data *data, bool on,
 
 		return ret;
 	}
+
+	if (on) {
+		/* If _resume() was not called, enable measurement now. */
+		if (data->als_ps_need_en) {
+			ret = rpr0521_als_enable(data, RPR0521_MODE_ALS_ENABLE);
+			if (ret)
+				return ret;
+			data->als_ps_need_en = false;
+		}
+
+		if (data->pxs_ps_need_en) {
+			ret = rpr0521_pxs_enable(data, RPR0521_MODE_PXS_ENABLE);
+			if (ret)
+				return ret;
+			data->pxs_ps_need_en = false;
+		}
+	}
 #endif
 	return 0;
 }
@@ -425,12 +436,14 @@ static int rpr0521_init(struct rpr0521_data *data)
 		return ret;
 	}
 
+#ifndef CONFIG_PM
 	ret = rpr0521_als_enable(data, RPR0521_MODE_ALS_ENABLE);
 	if (ret < 0)
 		return ret;
 	ret = rpr0521_pxs_enable(data, RPR0521_MODE_PXS_ENABLE);
 	if (ret < 0)
 		return ret;
+#endif
 
 	return 0;
 }
@@ -560,9 +573,16 @@ static int rpr0521_runtime_suspend(struct device *dev)
 	struct rpr0521_data *data = iio_priv(indio_dev);
 	int ret;
 
-	/* disable channels and sets {als,pxs}_dev_en to false */
 	mutex_lock(&data->lock);
+	/* If measurements are enabled, enable them on resume */
+	if (!data->als_need_dis)
+		data->als_ps_need_en = data->als_dev_en;
+	if (!data->pxs_need_dis)
+		data->pxs_ps_need_en = data->pxs_dev_en;
+
+	/* disable channels and sets {als,pxs}_dev_en to false */
 	ret = rpr0521_poweroff(data);
+	regcache_mark_dirty(data->regmap);
 	mutex_unlock(&data->lock);
 
 	return ret;
@@ -574,6 +594,7 @@ static int rpr0521_runtime_resume(struct device *dev)
 	struct rpr0521_data *data = iio_priv(indio_dev);
 	int ret;
 
+	regcache_sync(data->regmap);
 	if (data->als_ps_need_en) {
 		ret = rpr0521_als_enable(data, RPR0521_MODE_ALS_ENABLE);
 		if (ret < 0)
@@ -587,6 +608,7 @@ static int rpr0521_runtime_resume(struct device *dev)
 			return ret;
 		data->pxs_ps_need_en = false;
 	}
+	msleep(100);	//wait for first measurement result
 
 	return 0;
 }
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v5 4/9] iio: light: rpr0521 magic number to sizeof() on value read
  2017-05-18 12:12 [PATCH v5 0/9] iio: light: rpr0521 Mikko Koivunen
                   ` (2 preceding siblings ...)
  2017-05-18 12:12 ` [PATCH v5 3/9] iio: light: rpr0521 on-off sequence change for CONFIG_PM Mikko Koivunen
@ 2017-05-18 12:12 ` Mikko Koivunen
  2017-05-21 11:11   ` Jonathan Cameron
  2017-05-18 12:12 ` [PATCH v5 5/9] iio: light: rpr0521 whitespace fixes Mikko Koivunen
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 22+ messages in thread
From: Mikko Koivunen @ 2017-05-18 12:12 UTC (permalink / raw)
  To: jic23; +Cc: pmeerw, knaack.h, lars, Daniel Baluta, linux-iio, Mikko Koivunen

Changed magic number to sizeof() on value read.

Signed-off-by: Mikko Koivunen <mikko.koivunen@fi.rohmeurope.com>
---

 drivers/iio/light/rpr0521.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
index 9219819..38095d7 100644
--- a/drivers/iio/light/rpr0521.c
+++ b/drivers/iio/light/rpr0521.c
@@ -356,7 +356,7 @@ static int rpr0521_read_raw(struct iio_dev *indio_dev,
 
 		ret = regmap_bulk_read(data->regmap,
 				       rpr0521_data_reg[chan->address].address,
-				       &raw_data, 2);
+				       &raw_data, sizeof(raw_data));
 		if (ret < 0) {
 			rpr0521_set_power_state(data, false, device_mask);
 			mutex_unlock(&data->lock);
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v5 5/9] iio: light: rpr0521 whitespace fixes
  2017-05-18 12:12 [PATCH v5 0/9] iio: light: rpr0521 Mikko Koivunen
                   ` (3 preceding siblings ...)
  2017-05-18 12:12 ` [PATCH v5 4/9] iio: light: rpr0521 magic number to sizeof() on value read Mikko Koivunen
@ 2017-05-18 12:12 ` Mikko Koivunen
  2017-05-21 11:12   ` Jonathan Cameron
  2017-05-18 12:12 ` [PATCH v5 6/9] iio: light: rpr0521 sample_frequency read/write Mikko Koivunen
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 22+ messages in thread
From: Mikko Koivunen @ 2017-05-18 12:12 UTC (permalink / raw)
  To: jic23; +Cc: pmeerw, knaack.h, lars, Daniel Baluta, linux-iio, Mikko Koivunen

Just whitespace change, no functional changes.

Signed-off-by: Mikko Koivunen <mikko.koivunen@fi.rohmeurope.com>
---

 drivers/iio/light/rpr0521.c |    3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
index 38095d7..52c559d 100644
--- a/drivers/iio/light/rpr0521.c
+++ b/drivers/iio/light/rpr0521.c
@@ -371,6 +371,7 @@ static int rpr0521_read_raw(struct iio_dev *indio_dev,
 		*val = le16_to_cpu(raw_data);
 
 		return IIO_VAL_INT;
+
 	case IIO_CHAN_INFO_SCALE:
 		mutex_lock(&data->lock);
 		ret = rpr0521_get_gain(data, chan->address, val, val2);
@@ -379,6 +380,7 @@ static int rpr0521_read_raw(struct iio_dev *indio_dev,
 			return ret;
 
 		return IIO_VAL_INT_PLUS_MICRO;
+
 	default:
 		return -EINVAL;
 	}
@@ -398,6 +400,7 @@ static int rpr0521_write_raw(struct iio_dev *indio_dev,
 		mutex_unlock(&data->lock);
 
 		return ret;
+
 	default:
 		return -EINVAL;
 	}
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v5 6/9] iio: light: rpr0521 sample_frequency read/write
  2017-05-18 12:12 [PATCH v5 0/9] iio: light: rpr0521 Mikko Koivunen
                   ` (4 preceding siblings ...)
  2017-05-18 12:12 ` [PATCH v5 5/9] iio: light: rpr0521 whitespace fixes Mikko Koivunen
@ 2017-05-18 12:12 ` Mikko Koivunen
  2017-05-21 11:14   ` Jonathan Cameron
  2017-05-18 12:12 ` [PATCH v5 7/9] iio: light: rpr0521 proximity offset read/write Mikko Koivunen
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 22+ messages in thread
From: Mikko Koivunen @ 2017-05-18 12:12 UTC (permalink / raw)
  To: jic23; +Cc: pmeerw, knaack.h, lars, Daniel Baluta, linux-iio, Mikko Koivunen

Add sysfs read/write sample frequency.

Signed-off-by: Mikko Koivunen <mikko.koivunen@fi.rohmeurope.com>
---
Patch v1->v2 changes:
multiline comments fixed
Patch v2->v3 changes:
multiline comments fixed
Empty line fixes on switch-case.
Changed if-elseif-else to switch-case.
checkpatch.pl OK
Tested on LeMaker HiKey with AOSP7.1 kernel 4.4.
Builds ok with torvalds/linux feb 27.

Patch v3->v4 changes:
indent fix

 drivers/iio/light/rpr0521.c |  117 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 117 insertions(+)

diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
index 52c559d..0aac4bc 100644
--- a/drivers/iio/light/rpr0521.c
+++ b/drivers/iio/light/rpr0521.c
@@ -132,6 +132,30 @@ static const struct rpr0521_gain_info {
 	},
 };
 
+struct rpr0521_samp_freq {
+	int	als_hz;
+	int	als_uhz;
+	int	pxs_hz;
+	int	pxs_uhz;
+};
+
+static const struct rpr0521_samp_freq rpr0521_samp_freq_i[13] = {
+/*	{ALS, PXS},		   W==currently writable option */
+	{0, 0, 0, 0},		/* W0000, 0=standby */
+	{0, 0, 100, 0},		/*  0001 */
+	{0, 0, 25, 0},		/*  0010 */
+	{0, 0, 10, 0},		/*  0011 */
+	{0, 0, 2, 500000},	/*  0100 */
+	{10, 0, 20, 0},		/*  0101 */
+	{10, 0, 10, 0},		/* W0110 */
+	{10, 0, 2, 500000},	/*  0111 */
+	{2, 500000, 20, 0},	/*  1000, measurement 100ms, sleep 300ms */
+	{2, 500000, 10, 0},	/*  1001, measurement 100ms, sleep 300ms */
+	{2, 500000, 0, 0},	/*  1010, high sensitivity mode */
+	{2, 500000, 2, 500000},	/* W1011, high sensitivity mode */
+	{20, 0, 20, 0}	/* 1100, ALS_data x 0.5, see specification P.18 */
+};
+
 struct rpr0521_data {
 	struct i2c_client *client;
 
@@ -154,9 +178,16 @@ struct rpr0521_data {
 static IIO_CONST_ATTR(in_intensity_scale_available, RPR0521_ALS_SCALE_AVAIL);
 static IIO_CONST_ATTR(in_proximity_scale_available, RPR0521_PXS_SCALE_AVAIL);
 
+/*
+ * Start with easy freq first, whole table of freq combinations is more
+ * complicated.
+ */
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("2.5 10");
+
 static struct attribute *rpr0521_attributes[] = {
 	&iio_const_attr_in_intensity_scale_available.dev_attr.attr,
 	&iio_const_attr_in_proximity_scale_available.dev_attr.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
 	NULL,
 };
 
@@ -172,6 +203,7 @@ static const struct iio_chan_spec rpr0521_channels[] = {
 		.channel2 = IIO_MOD_LIGHT_BOTH,
 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 			BIT(IIO_CHAN_INFO_SCALE),
+		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
 	},
 	{
 		.type = IIO_INTENSITY,
@@ -180,12 +212,14 @@ static const struct iio_chan_spec rpr0521_channels[] = {
 		.channel2 = IIO_MOD_LIGHT_IR,
 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 			BIT(IIO_CHAN_INFO_SCALE),
+		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
 	},
 	{
 		.type = IIO_PROXIMITY,
 		.address = RPR0521_CHAN_PXS,
 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 			BIT(IIO_CHAN_INFO_SCALE),
+		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
 	}
 };
 
@@ -331,6 +365,72 @@ static int rpr0521_set_gain(struct rpr0521_data *data, int chan,
 				  idx << rpr0521_gain[chan].shift);
 }
 
+static int rpr0521_read_samp_freq(struct rpr0521_data *data,
+				enum iio_chan_type chan_type,
+			    int *val, int *val2)
+{
+	int reg, ret;
+
+	ret = regmap_read(data->regmap, RPR0521_REG_MODE_CTRL, &reg);
+	if (ret < 0)
+		return ret;
+
+	reg &= RPR0521_MODE_MEAS_TIME_MASK;
+	if (reg >= ARRAY_SIZE(rpr0521_samp_freq_i))
+		return -EINVAL;
+
+	switch (chan_type) {
+	case IIO_INTENSITY:
+		*val = rpr0521_samp_freq_i[reg].als_hz;
+		*val2 = rpr0521_samp_freq_i[reg].als_uhz;
+		return 0;
+
+	case IIO_PROXIMITY:
+		*val = rpr0521_samp_freq_i[reg].pxs_hz;
+		*val2 = rpr0521_samp_freq_i[reg].pxs_uhz;
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int rpr0521_write_samp_freq_common(struct rpr0521_data *data,
+				enum iio_chan_type chan_type,
+				int val, int val2)
+{
+	int i;
+
+	/*
+	 * Ignore channel
+	 * both pxs and als are setup only to same freq because of simplicity
+	 */
+	switch (val) {
+	case 0:
+		i = 0;
+		break;
+
+	case 2:
+		if (val2 != 500000)
+			return -EINVAL;
+
+		i = 11;
+		break;
+
+	case 10:
+		i = 6;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return regmap_update_bits(data->regmap,
+		RPR0521_REG_MODE_CTRL,
+		RPR0521_MODE_MEAS_TIME_MASK,
+		i);
+}
+
 static int rpr0521_read_raw(struct iio_dev *indio_dev,
 			    struct iio_chan_spec const *chan, int *val,
 			    int *val2, long mask)
@@ -381,6 +481,15 @@ static int rpr0521_read_raw(struct iio_dev *indio_dev,
 
 		return IIO_VAL_INT_PLUS_MICRO;
 
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		mutex_lock(&data->lock);
+		ret = rpr0521_read_samp_freq(data, chan->type, val, val2);
+		mutex_unlock(&data->lock);
+		if (ret < 0)
+			return ret;
+
+		return IIO_VAL_INT_PLUS_MICRO;
+
 	default:
 		return -EINVAL;
 	}
@@ -401,6 +510,14 @@ static int rpr0521_write_raw(struct iio_dev *indio_dev,
 
 		return ret;
 
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		mutex_lock(&data->lock);
+		ret = rpr0521_write_samp_freq_common(data, chan->type,
+						     val, val2);
+		mutex_unlock(&data->lock);
+
+		return ret;
+
 	default:
 		return -EINVAL;
 	}
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v5 7/9] iio: light: rpr0521 proximity offset read/write
  2017-05-18 12:12 [PATCH v5 0/9] iio: light: rpr0521 Mikko Koivunen
                   ` (5 preceding siblings ...)
  2017-05-18 12:12 ` [PATCH v5 6/9] iio: light: rpr0521 sample_frequency read/write Mikko Koivunen
@ 2017-05-18 12:12 ` Mikko Koivunen
  2017-05-21 11:15   ` Jonathan Cameron
  2017-05-18 12:12 ` [PATCH v5 8/9] iio: light: rpr0521 channel numbers reordered Mikko Koivunen
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 22+ messages in thread
From: Mikko Koivunen @ 2017-05-18 12:12 UTC (permalink / raw)
  To: jic23; +Cc: pmeerw, knaack.h, lars, Daniel Baluta, linux-iio, Mikko Koivunen

Add sysfs read/write proximity offset feature. Offset is read/write from
sensor registers. Values are proximity raw 10-bit values. After applying
offset value, output values will be (measured_raw - offset_value). Output
values are unsigned so offset value doesn't make output negative.

Signed-off-by: Mikko Koivunen <mikko.koivunen@fi.rohmeurope.com>
---
Patch v1->v2 changes:
read/write_ps_offset() rewritten with:
 -  static, __le16, cpu_to_le16(), sizeof()
Patch v2->v3 changes:
RPR0521_*REG_*PS_OFFSET_LSB
removed RPR0521_REG_PS_OFFSET_MSB
Whitespace changes

checkpatch.pl OK
Tested on LeMaker HiKey with AOSP7.1 kernel 4.4.
Builds ok with torvalds/linux feb 27.

 drivers/iio/light/rpr0521.c |   52 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
index 0aac4bc..83b24d1 100644
--- a/drivers/iio/light/rpr0521.c
+++ b/drivers/iio/light/rpr0521.c
@@ -30,6 +30,7 @@
 #define RPR0521_REG_PXS_DATA		0x44 /* 16-bit, little endian */
 #define RPR0521_REG_ALS_DATA0		0x46 /* 16-bit, little endian */
 #define RPR0521_REG_ALS_DATA1		0x48 /* 16-bit, little endian */
+#define RPR0521_REG_PS_OFFSET_LSB	0x53
 #define RPR0521_REG_ID			0x92
 
 #define RPR0521_MODE_ALS_MASK		BIT(7)
@@ -218,6 +219,7 @@ static const struct iio_chan_spec rpr0521_channels[] = {
 		.type = IIO_PROXIMITY,
 		.address = RPR0521_CHAN_PXS,
 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+			BIT(IIO_CHAN_INFO_OFFSET) |
 			BIT(IIO_CHAN_INFO_SCALE),
 		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
 	}
@@ -431,6 +433,40 @@ static int rpr0521_write_samp_freq_common(struct rpr0521_data *data,
 		i);
 }
 
+static int rpr0521_read_ps_offset(struct rpr0521_data *data, int *offset)
+{
+	int ret;
+	__le16 buffer;
+
+	ret = regmap_bulk_read(data->regmap,
+		RPR0521_REG_PS_OFFSET_LSB, &buffer, sizeof(buffer));
+
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Failed to read PS OFFSET register\n");
+		return ret;
+	}
+	*offset = le16_to_cpu(buffer);
+
+	return ret;
+}
+
+static int rpr0521_write_ps_offset(struct rpr0521_data *data, int offset)
+{
+	int ret;
+	__le16 buffer;
+
+	buffer = cpu_to_le16(offset & 0x3ff);
+	ret = regmap_raw_write(data->regmap,
+		RPR0521_REG_PS_OFFSET_LSB, &buffer, sizeof(buffer));
+
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Failed to write PS OFFSET register\n");
+		return ret;
+	}
+
+	return ret;
+}
+
 static int rpr0521_read_raw(struct iio_dev *indio_dev,
 			    struct iio_chan_spec const *chan, int *val,
 			    int *val2, long mask)
@@ -490,6 +526,15 @@ static int rpr0521_read_raw(struct iio_dev *indio_dev,
 
 		return IIO_VAL_INT_PLUS_MICRO;
 
+	case IIO_CHAN_INFO_OFFSET:
+		mutex_lock(&data->lock);
+		ret = rpr0521_read_ps_offset(data, val);
+		mutex_unlock(&data->lock);
+		if (ret < 0)
+			return ret;
+
+		return IIO_VAL_INT;
+
 	default:
 		return -EINVAL;
 	}
@@ -518,6 +563,13 @@ static int rpr0521_write_raw(struct iio_dev *indio_dev,
 
 		return ret;
 
+	case IIO_CHAN_INFO_OFFSET:
+		mutex_lock(&data->lock);
+		ret = rpr0521_write_ps_offset(data, val);
+		mutex_unlock(&data->lock);
+
+		return ret;
+
 	default:
 		return -EINVAL;
 	}
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v5 8/9] iio: light: rpr0521 channel numbers reordered
  2017-05-18 12:12 [PATCH v5 0/9] iio: light: rpr0521 Mikko Koivunen
                   ` (6 preceding siblings ...)
  2017-05-18 12:12 ` [PATCH v5 7/9] iio: light: rpr0521 proximity offset read/write Mikko Koivunen
@ 2017-05-18 12:12 ` Mikko Koivunen
  2017-05-21 11:16   ` Jonathan Cameron
  2017-05-18 12:12 ` [PATCH v5 9/9] iio: light: rpr0521 triggered buffer Mikko Koivunen
  2017-05-18 12:20 ` [PATCH v5 0/9] iio: light: rpr0521 Daniel Baluta
  9 siblings, 1 reply; 22+ messages in thread
From: Mikko Koivunen @ 2017-05-18 12:12 UTC (permalink / raw)
  To: jic23; +Cc: pmeerw, knaack.h, lars, Daniel Baluta, linux-iio, Mikko Koivunen

Move proximity channel from last to first in structs to avoid confusion
later with buffered triggers. Proximity data output is first in rpr0521
register map.

Signed-off-by: Mikko Koivunen <mikko.koivunen@fi.rohmeurope.com>
---

 drivers/iio/light/rpr0521.c |   40 ++++++++++++++++++++--------------------
 1 file changed, 20 insertions(+), 20 deletions(-)

diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
index 83b24d1..9d0c2e8 100644
--- a/drivers/iio/light/rpr0521.c
+++ b/drivers/iio/light/rpr0521.c
@@ -78,9 +78,9 @@ static const struct rpr0521_gain rpr0521_pxs_gain[3] = {
 };
 
 enum rpr0521_channel {
+	RPR0521_CHAN_PXS,
 	RPR0521_CHAN_ALS_DATA0,
 	RPR0521_CHAN_ALS_DATA1,
-	RPR0521_CHAN_PXS,
 };
 
 struct rpr0521_reg_desc {
@@ -89,6 +89,10 @@ struct rpr0521_reg_desc {
 };
 
 static const struct rpr0521_reg_desc rpr0521_data_reg[] = {
+	[RPR0521_CHAN_PXS]	= {
+		.address	= RPR0521_REG_PXS_DATA,
+		.device_mask	= RPR0521_MODE_PXS_MASK,
+	},
 	[RPR0521_CHAN_ALS_DATA0] = {
 		.address	= RPR0521_REG_ALS_DATA0,
 		.device_mask	= RPR0521_MODE_ALS_MASK,
@@ -97,10 +101,6 @@ static const struct rpr0521_reg_desc rpr0521_data_reg[] = {
 		.address	= RPR0521_REG_ALS_DATA1,
 		.device_mask	= RPR0521_MODE_ALS_MASK,
 	},
-	[RPR0521_CHAN_PXS]	= {
-		.address	= RPR0521_REG_PXS_DATA,
-		.device_mask	= RPR0521_MODE_PXS_MASK,
-	},
 };
 
 static const struct rpr0521_gain_info {
@@ -110,6 +110,13 @@ static const struct rpr0521_gain_info {
 	const struct rpr0521_gain *gain;
 	int size;
 } rpr0521_gain[] = {
+	[RPR0521_CHAN_PXS] = {
+		.reg	= RPR0521_REG_PXS_CTRL,
+		.mask	= RPR0521_PXS_GAIN_MASK,
+		.shift	= RPR0521_PXS_GAIN_SHIFT,
+		.gain	= rpr0521_pxs_gain,
+		.size	= ARRAY_SIZE(rpr0521_pxs_gain),
+	},
 	[RPR0521_CHAN_ALS_DATA0] = {
 		.reg	= RPR0521_REG_ALS_CTRL,
 		.mask	= RPR0521_ALS_DATA0_GAIN_MASK,
@@ -124,13 +131,6 @@ static const struct rpr0521_gain_info {
 		.gain	= rpr0521_als_gain,
 		.size	= ARRAY_SIZE(rpr0521_als_gain),
 	},
-	[RPR0521_CHAN_PXS] = {
-		.reg	= RPR0521_REG_PXS_CTRL,
-		.mask	= RPR0521_PXS_GAIN_MASK,
-		.shift	= RPR0521_PXS_GAIN_SHIFT,
-		.gain	= rpr0521_pxs_gain,
-		.size	= ARRAY_SIZE(rpr0521_pxs_gain),
-	},
 };
 
 struct rpr0521_samp_freq {
@@ -198,6 +198,14 @@ static const struct attribute_group rpr0521_attribute_group = {
 
 static const struct iio_chan_spec rpr0521_channels[] = {
 	{
+		.type = IIO_PROXIMITY,
+		.address = RPR0521_CHAN_PXS,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+			BIT(IIO_CHAN_INFO_OFFSET) |
+			BIT(IIO_CHAN_INFO_SCALE),
+		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+	},
+	{
 		.type = IIO_INTENSITY,
 		.modified = 1,
 		.address = RPR0521_CHAN_ALS_DATA0,
@@ -215,14 +223,6 @@ static const struct iio_chan_spec rpr0521_channels[] = {
 			BIT(IIO_CHAN_INFO_SCALE),
 		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
 	},
-	{
-		.type = IIO_PROXIMITY,
-		.address = RPR0521_CHAN_PXS,
-		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
-			BIT(IIO_CHAN_INFO_OFFSET) |
-			BIT(IIO_CHAN_INFO_SCALE),
-		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
-	}
 };
 
 static int rpr0521_als_enable(struct rpr0521_data *data, u8 status)
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v5 9/9] iio: light: rpr0521 triggered buffer
  2017-05-18 12:12 [PATCH v5 0/9] iio: light: rpr0521 Mikko Koivunen
                   ` (7 preceding siblings ...)
  2017-05-18 12:12 ` [PATCH v5 8/9] iio: light: rpr0521 channel numbers reordered Mikko Koivunen
@ 2017-05-18 12:12 ` Mikko Koivunen
  2017-05-21 11:34   ` Jonathan Cameron
  2017-05-18 12:20 ` [PATCH v5 0/9] iio: light: rpr0521 Daniel Baluta
  9 siblings, 1 reply; 22+ messages in thread
From: Mikko Koivunen @ 2017-05-18 12:12 UTC (permalink / raw)
  To: jic23; +Cc: pmeerw, knaack.h, lars, Daniel Baluta, linux-iio, Mikko Koivunen

Set up and use triggered buffer if there is irq defined for device in
device tree. Trigger producer triggers from rpr0521 drdy interrupt line.
Trigger consumer reads rpr0521 data to scan buffer.
Depends on previous commits of _scale and _offset.

Signed-off-by: Mikko Koivunen <mikko.koivunen@fi.rohmeurope.com>
---
Patch v2->v3 changes:
.shift = 0 removed
reordered functions to remove forward declarations
whitespace changes
refactored some update_bits->write, no "err = err || *"-pattern
refactored trigger_consumer_handler
reordered _probe() and _remove()
added int clear on poweroff()
checkpatch.pl OK
Tested on LeMaker HiKey with AOSP7.1 kernel 4.4.
Builds ok with torvalds/linux feb 27.

Patch v3->v4 changes:
Moved sensor on/off commands to buffer preenable and postdisable
 - Since drdy happens only on measurement data ready and register writes
   are cached, the trigger producer doesn't care of suspend/resume state.
available_scan_masks added
indent fix
checkpatch.pl OK
Lightly re-tested on LeMaker HiKey with AOSP7.1 kernel 4.4.
Builds ok with torvalds/linux feb 27.

 drivers/iio/light/rpr0521.c |  294 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 291 insertions(+), 3 deletions(-)

diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
index 9d0c2e8..1ef6060 100644
--- a/drivers/iio/light/rpr0521.c
+++ b/drivers/iio/light/rpr0521.c
@@ -9,7 +9,7 @@
  *
  * IIO driver for RPR-0521RS (7-bit I2C slave address 0x38).
  *
- * TODO: illuminance channel, buffer
+ * TODO: illuminance channel
  */
 
 #include <linux/module.h>
@@ -20,6 +20,10 @@
 #include <linux/acpi.h>
 
 #include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
 #include <linux/iio/sysfs.h>
 #include <linux/pm_runtime.h>
 
@@ -30,6 +34,7 @@
 #define RPR0521_REG_PXS_DATA		0x44 /* 16-bit, little endian */
 #define RPR0521_REG_ALS_DATA0		0x46 /* 16-bit, little endian */
 #define RPR0521_REG_ALS_DATA1		0x48 /* 16-bit, little endian */
+#define RPR0521_REG_INTERRUPT		0x4A
 #define RPR0521_REG_PS_OFFSET_LSB	0x53
 #define RPR0521_REG_ID			0x92
 
@@ -42,16 +47,29 @@
 #define RPR0521_ALS_DATA1_GAIN_SHIFT	2
 #define RPR0521_PXS_GAIN_MASK		GENMASK(5, 4)
 #define RPR0521_PXS_GAIN_SHIFT		4
+#define RPR0521_PXS_PERSISTENCE_MASK	GENMASK(3, 0)
+#define RPR0521_INTERRUPT_INT_TRIG_PS_MASK	BIT(0)
+#define RPR0521_INTERRUPT_INT_TRIG_ALS_MASK	BIT(1)
+#define RPR0521_INTERRUPT_INT_REASSERT_MASK	BIT(3)
 
 #define RPR0521_MODE_ALS_ENABLE		BIT(7)
 #define RPR0521_MODE_ALS_DISABLE	0x00
 #define RPR0521_MODE_PXS_ENABLE		BIT(6)
 #define RPR0521_MODE_PXS_DISABLE	0x00
+#define RPR0521_PXS_PERSISTENCE_DRDY	0x00
+
+#define RPR0521_INTERRUPT_INT_TRIG_PS_ENABLE	BIT(0)
+#define RPR0521_INTERRUPT_INT_TRIG_PS_DISABLE	0x00
+#define RPR0521_INTERRUPT_INT_TRIG_ALS_ENABLE	BIT(1)
+#define RPR0521_INTERRUPT_INT_TRIG_ALS_DISABLE	0x00
+#define RPR0521_INTERRUPT_INT_REASSERT_ENABLE	BIT(3)
+#define RPR0521_INTERRUPT_INT_REASSERT_DISABLE	0x00
 
 #define RPR0521_MANUFACT_ID		0xE0
 #define RPR0521_DEFAULT_MEAS_TIME	0x06 /* ALS - 100ms, PXS - 100ms */
 
 #define RPR0521_DRV_NAME		"RPR0521"
+#define RPR0521_IRQ_NAME		"rpr0521_event"
 #define RPR0521_REGMAP_NAME		"rpr0521_regmap"
 
 #define RPR0521_SLEEP_DELAY_MS	2000
@@ -167,6 +185,9 @@ struct rpr0521_data {
 	bool als_dev_en;
 	bool pxs_dev_en;
 
+	struct iio_trigger *drdy_trigger0;
+	bool drdy_trigger_on;
+
 	/* optimize runtime pm ops - enable/disable device only if needed */
 	bool als_ps_need_en;
 	bool pxs_ps_need_en;
@@ -196,6 +217,19 @@ static const struct attribute_group rpr0521_attribute_group = {
 	.attrs = rpr0521_attributes,
 };
 
+/* Order of the channel data in buffer */
+enum rpr0521_scan_index_order {
+	RPR0521_CHAN_INDEX_PXS,
+	RPR0521_CHAN_INDEX_BOTH,
+	RPR0521_CHAN_INDEX_IR,
+};
+
+static const unsigned long rpr0521_available_scan_masks[] = {
+	BIT(RPR0521_CHAN_INDEX_PXS) | BIT(RPR0521_CHAN_INDEX_BOTH) |
+	BIT(RPR0521_CHAN_INDEX_IR),
+	0
+};
+
 static const struct iio_chan_spec rpr0521_channels[] = {
 	{
 		.type = IIO_PROXIMITY,
@@ -204,6 +238,13 @@ static const struct iio_chan_spec rpr0521_channels[] = {
 			BIT(IIO_CHAN_INFO_OFFSET) |
 			BIT(IIO_CHAN_INFO_SCALE),
 		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.scan_index = RPR0521_CHAN_INDEX_PXS,
+		.scan_type = {
+			.sign = 'u',
+			.realbits = 16,
+			.storagebits = 16,
+			.endianness = IIO_LE,
+		},
 	},
 	{
 		.type = IIO_INTENSITY,
@@ -213,6 +254,13 @@ static const struct iio_chan_spec rpr0521_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 = RPR0521_CHAN_INDEX_BOTH,
+		.scan_type = {
+			.sign = 'u',
+			.realbits = 16,
+			.storagebits = 16,
+			.endianness = IIO_LE,
+		},
 	},
 	{
 		.type = IIO_INTENSITY,
@@ -222,6 +270,13 @@ static const struct iio_chan_spec rpr0521_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 = RPR0521_CHAN_INDEX_IR,
+		.scan_type = {
+			.sign = 'u',
+			.realbits = 16,
+			.storagebits = 16,
+			.endianness = IIO_LE,
+		},
 	},
 };
 
@@ -330,6 +385,150 @@ static int rpr0521_set_power_state(struct rpr0521_data *data, bool on,
 	return 0;
 }
 
+/* IRQ to trigger handler */
+static irqreturn_t rpr0521_drdy_irq_handler(int irq, void *private)
+{
+	struct iio_dev *indio_dev = private;
+	struct rpr0521_data *data = iio_priv(indio_dev);
+
+	/*
+	 * Sometimes static on floating (hi-z) interrupt line causes interrupt.
+	 * Notify trigger0 consumers/subscribers only if trigger has been
+	 * enabled. This is to prevent i2c writes to sensor which is actually
+	 * powered off.
+	 */
+	if (data->drdy_trigger_on)
+		iio_trigger_poll(data->drdy_trigger0);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t rpr0521_trigger_consumer_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct rpr0521_data *data = iio_priv(indio_dev);
+	int err;
+
+	u8 buffer[16]; /* 3 16-bit channels + padding + ts */
+
+	err = regmap_bulk_read(data->regmap, RPR0521_REG_PXS_DATA,
+		&buffer,
+		(3 * 2) + 1);	/* 3 * 16-bit + (discarded) int clear reg. */
+	if (!err)
+		iio_push_to_buffers_with_timestamp(indio_dev,
+						   buffer, pf->timestamp);
+	else
+		dev_err(&data->client->dev,
+			"Trigger consumer can't read from sensor.\n");
+
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
+static int rpr0521_write_int_enable(struct rpr0521_data *data)
+{
+	int err;
+
+	/* Interrupt after each measurement */
+	err = regmap_update_bits(data->regmap, RPR0521_REG_PXS_CTRL,
+		RPR0521_PXS_PERSISTENCE_MASK,
+		RPR0521_PXS_PERSISTENCE_DRDY);
+	if (err) {
+		dev_err(&data->client->dev, "PS control reg write fail.\n");
+		return -EBUSY;
+		}
+
+	/* Ignore latch and mode because of drdy */
+	err = regmap_write(data->regmap, RPR0521_REG_INTERRUPT,
+		RPR0521_INTERRUPT_INT_REASSERT_DISABLE |
+		RPR0521_INTERRUPT_INT_TRIG_ALS_DISABLE |
+		RPR0521_INTERRUPT_INT_TRIG_PS_ENABLE
+		);
+	if (err) {
+		dev_err(&data->client->dev, "Interrupt setup write fail.\n");
+		return -EBUSY;
+		}
+
+	return 0;
+}
+
+static int rpr0521_write_int_disable(struct rpr0521_data *data)
+{
+	/* Don't care of clearing mode, assert and latch. */
+	return regmap_write(data->regmap, RPR0521_REG_INTERRUPT,
+				RPR0521_INTERRUPT_INT_TRIG_ALS_DISABLE |
+				RPR0521_INTERRUPT_INT_TRIG_PS_DISABLE
+				);
+}
+
+/*
+ * Trigger producer enable / disable. Note that there will be trigs only when
+ * measurement data is ready to be read.
+ */
+static int rpr0521_pxs_drdy_set_state(struct iio_trigger *trigger,
+	bool enable_drdy)
+{
+	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trigger);
+	struct rpr0521_data *data = iio_priv(indio_dev);
+	int err;
+
+	if (enable_drdy)
+		err = rpr0521_write_int_enable(data);
+	else
+		err = rpr0521_write_int_disable(data);
+	if (err)
+		dev_err(&data->client->dev, "rpr0521_pxs_drdy_set_state failed\n");
+	else
+		data->drdy_trigger_on = enable_drdy;
+
+	return err;
+}
+
+static const struct iio_trigger_ops rpr0521_trigger_ops = {
+	.set_trigger_state = rpr0521_pxs_drdy_set_state,
+	.owner = THIS_MODULE,
+	};
+
+
+static int rpr0521_buffer_preenable(struct iio_dev *indio_dev)
+{
+	int err;
+	struct rpr0521_data *data = iio_priv(indio_dev);
+
+	mutex_lock(&data->lock);
+	err = rpr0521_set_power_state(data, true,
+		(RPR0521_MODE_PXS_MASK | RPR0521_MODE_ALS_MASK));
+	mutex_unlock(&data->lock);
+	if (err)
+		dev_err(&data->client->dev, "_buffer_preenable fail\n");
+
+	return err;
+}
+
+static int rpr0521_buffer_postdisable(struct iio_dev *indio_dev)
+{
+	int err;
+	struct rpr0521_data *data = iio_priv(indio_dev);
+
+	mutex_lock(&data->lock);
+	err = rpr0521_set_power_state(data, false,
+		(RPR0521_MODE_PXS_MASK | RPR0521_MODE_ALS_MASK));
+	mutex_unlock(&data->lock);
+	if (err)
+		dev_err(&data->client->dev, "_buffer_postdisable fail\n");
+
+	return err;
+}
+
+static const struct iio_buffer_setup_ops rpr0521_buffer_setup_ops = {
+	.preenable = rpr0521_buffer_preenable,
+	.postenable = iio_triggered_buffer_postenable,
+	.predisable = iio_triggered_buffer_predisable,
+	.postdisable = rpr0521_buffer_postdisable,
+};
+
 static int rpr0521_get_gain(struct rpr0521_data *data, int chan,
 			    int *val, int *val2)
 {
@@ -481,6 +680,9 @@ static int rpr0521_read_raw(struct iio_dev *indio_dev,
 		if (chan->type != IIO_INTENSITY && chan->type != IIO_PROXIMITY)
 			return -EINVAL;
 
+		if (iio_buffer_enabled(indio_dev))
+			return -EBUSY;
+
 		device_mask = rpr0521_data_reg[chan->address].device_mask;
 
 		mutex_lock(&data->lock);
@@ -623,6 +825,7 @@ static int rpr0521_init(struct rpr0521_data *data)
 static int rpr0521_poweroff(struct rpr0521_data *data)
 {
 	int ret;
+	int tmp;
 
 	ret = regmap_update_bits(data->regmap, RPR0521_REG_MODE_CTRL,
 				 RPR0521_MODE_ALS_MASK |
@@ -635,6 +838,16 @@ static int rpr0521_poweroff(struct rpr0521_data *data)
 	data->als_dev_en = false;
 	data->pxs_dev_en = false;
 
+	/*
+	 * Int pin keeps state after power off. Set pin to high impedance
+	 * mode to prevent power drain.
+	 */
+	ret = regmap_read(data->regmap, RPR0521_REG_INTERRUPT, &tmp);
+	if (ret) {
+		dev_err(&data->client->dev, "Failed to reset int pin.\n");
+		return ret;
+	}
+
 	return 0;
 }
 
@@ -707,12 +920,78 @@ static int rpr0521_probe(struct i2c_client *client,
 	pm_runtime_set_autosuspend_delay(&client->dev, RPR0521_SLEEP_DELAY_MS);
 	pm_runtime_use_autosuspend(&client->dev);
 
+	/*
+	 * If sensor write/read  is needed in _probe after _use_autosuspend,
+	 * sensor needs to be _resumed first using rpr0521_set_power_state().
+	 */
+
+	/* IRQ to trigger setup */
+	if (client->irq) {
+		/* Trigger0 producer setup */
+		data->drdy_trigger0 = devm_iio_trigger_alloc(
+			indio_dev->dev.parent,
+			"%s-dev%d", indio_dev->name, indio_dev->id);
+		if (!data->drdy_trigger0) {
+			ret = -ENOMEM;
+			goto err_pm_disable;
+		}
+		data->drdy_trigger0->dev.parent = indio_dev->dev.parent;
+		data->drdy_trigger0->ops = &rpr0521_trigger_ops;
+		indio_dev->available_scan_masks = rpr0521_available_scan_masks;
+		iio_trigger_set_drvdata(data->drdy_trigger0, indio_dev);
+
+		/* Ties irq to trigger producer handler. */
+		ret = devm_request_threaded_irq(&client->dev, client->irq,
+			rpr0521_drdy_irq_handler,
+			NULL,
+			IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+			RPR0521_IRQ_NAME,
+			indio_dev);
+		if (ret < 0) {
+			dev_err(&client->dev, "request irq %d for trigger0 failed\n",
+				client->irq);
+			goto err_pm_disable;
+			}
+
+		ret = iio_trigger_register(data->drdy_trigger0);
+		if (ret) {
+			dev_err(&client->dev, "iio trigger register failed\n");
+			goto err_pm_disable;
+		}
+
+		/*
+		 * Now whole pipe from physical interrupt (irq defined by
+		 * devicetree to device) to trigger0 output is set up.
+		 */
+
+		/* Trigger consumer setup */
+		ret = iio_triggered_buffer_setup(indio_dev,
+			&iio_pollfunc_store_time,
+			rpr0521_trigger_consumer_handler,
+			&rpr0521_buffer_setup_ops);
+		if (ret < 0) {
+			dev_err(&client->dev, "iio triggered buffer setup failed\n");
+			/* Count on devm to free_irq */
+			goto err_iio_trigger_unregister;
+		}
+	}
+
 	ret = iio_device_register(indio_dev);
 	if (ret)
-		goto err_pm_disable;
+		goto err_iio_triggered_buffer_cleanup;
+
+	if (client->irq) {
+		/* Set trigger0 as current trigger (and inc refcount) */
+		indio_dev->trig = iio_trigger_get(data->drdy_trigger0);
+	}
 
 	return 0;
 
+err_iio_triggered_buffer_cleanup:
+	iio_triggered_buffer_cleanup(indio_dev);
+err_iio_trigger_unregister:
+	if (data->drdy_trigger0)
+		iio_trigger_unregister(data->drdy_trigger0);
 err_pm_disable:
 	pm_runtime_disable(&client->dev);
 	pm_runtime_set_suspended(&client->dev);
@@ -726,14 +1005,23 @@ err_poweroff:
 static int rpr0521_remove(struct i2c_client *client)
 {
 	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	struct rpr0521_data *data = iio_priv(indio_dev);
+
+	if (indio_dev->trig)
+		iio_trigger_put(indio_dev->trig);
 
 	iio_device_unregister(indio_dev);
 
+	iio_triggered_buffer_cleanup(indio_dev);
+
+	if (data->drdy_trigger0)
+		iio_trigger_unregister(data->drdy_trigger0);
+
 	pm_runtime_disable(&client->dev);
 	pm_runtime_set_suspended(&client->dev);
 	pm_runtime_put_noidle(&client->dev);
 
-	rpr0521_poweroff(iio_priv(indio_dev));
+	rpr0521_poweroff(data);
 
 	return 0;
 }
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* Re: [PATCH v5 0/9] iio: light: rpr0521
  2017-05-18 12:12 [PATCH v5 0/9] iio: light: rpr0521 Mikko Koivunen
                   ` (8 preceding siblings ...)
  2017-05-18 12:12 ` [PATCH v5 9/9] iio: light: rpr0521 triggered buffer Mikko Koivunen
@ 2017-05-18 12:20 ` Daniel Baluta
  9 siblings, 0 replies; 22+ messages in thread
From: Daniel Baluta @ 2017-05-18 12:20 UTC (permalink / raw)
  To: mikko.koivunen, jic23; +Cc: knaack.h, lars, pmeerw, linux-iio

T24gSm8sIDIwMTctMDUtMTggYXQgMTU6MTIgKzAzMDAsIE1pa2tvIEtvaXZ1bmVuIHdyb3RlOg0K
PiBQYXRjaHNldCB2NC0+djUgY2hhbmdlczoNCj4gwqAgY292ZXIgbGV0dGVyIGFkZGVkDQo+IMKg
IDAwMDIgKHZvaWQpIHJlbW92ZWQNCj4gDQo+IFBhdGNoc2V0IHYzLT52NCBjaGFuZ2VzOg0KPiDC
oCAwMDA2IGluZGVudCBmaXgNCj4gwqAgMDAwOSBvbi9vZmYgdG8gcHJlZW5hYmxlL3Bvc3RkaXNh
YmxlDQo+IA0KPiBQYXRjaHNldCB2Mi0+djMgY2hhbmdlczoNCj4gwqAgTW9yZSBjb21taXRzLCBu
dW1iZXJzIGNoYW5nZSwgb3JkZXIgY2hhbmdlDQo+IMKgIENoYW5nZXMgbGlzdGVkIGluIGluZGl2
aWR1YWwgY29tbWl0cw0KPiANCj4gTWlra28gS29pdnVuZW4gKDkpOg0KPiDCoCBpaW86IGxpZ2h0
OiBycHIwNTIxIGRpc2FibGUgc2Vuc29yIC1idWdmaXgNCj4gwqAgaWlvOiBsaWdodDogcnByMDUy
MSBwb3dlcm9mZiBmb3IgcHJvYmUgZmFpbHMNCj4gwqAgaWlvOiBsaWdodDogcnByMDUyMSBvbi1v
ZmYgc2VxdWVuY2UgY2hhbmdlIGZvciBDT05GSUdfUE0NCj4gwqAgaWlvOiBsaWdodDogcnByMDUy
MSBtYWdpYyBudW1iZXIgdG8gc2l6ZW9mKCkgb24gdmFsdWUgcmVhZA0KPiDCoCBpaW86IGxpZ2h0
OiBycHIwNTIxIHdoaXRlc3BhY2UgZml4ZXMNCj4gwqAgaWlvOiBsaWdodDogcnByMDUyMSBzYW1w
bGVfZnJlcXVlbmN5IHJlYWQvd3JpdGUNCj4gwqAgaWlvOiBsaWdodDogcnByMDUyMSBwcm94aW1p
dHkgb2Zmc2V0IHJlYWQvd3JpdGUNCj4gwqAgaWlvOiBsaWdodDogcnByMDUyMSBjaGFubmVsIG51
bWJlcnMgcmVvcmRlcmVkDQo+IMKgIGlpbzogbGlnaHQ6IHJwcjA1MjEgdHJpZ2dlcmVkIGJ1ZmZl
cg0KDQoNCkFja2VkLWJ5OiBEYW5pZWwgQmFsdXRhIDxkYW5pZWwuYmFsdXRhQG54cC5jb20+DQoN
ClRoYW5rcyBhIGxvdCBNaWtrbyENCg0KRGFuaWVsLg==

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v5 1/9] iio: light: rpr0521 disable sensor -bugfix
  2017-05-18 12:12 ` [PATCH v5 1/9] iio: light: rpr0521 disable sensor -bugfix Mikko Koivunen
@ 2017-05-21 11:06   ` Jonathan Cameron
  0 siblings, 0 replies; 22+ messages in thread
From: Jonathan Cameron @ 2017-05-21 11:06 UTC (permalink / raw)
  To: Mikko Koivunen; +Cc: pmeerw, knaack.h, lars, Daniel Baluta, linux-iio

On 18/05/17 13:12, Mikko Koivunen wrote:
> Sensor was marked enabled on each call even if the call was for disabling
> sensor.
> 
> Signed-off-by: Mikko Koivunen <mikko.koivunen@fi.rohmeurope.com>
I'm going to guess it 'kind of worked' before this and treat this
as a low urgency fix.  This is primarily to avoid having to delay
the non bug fix patches later in the series.

Applied to the togreg branch of iio.git and pushed out as testing
for the autobuilders to play with it.

Thanks,

Jonathan
> ---
> Tested on LeMaker HiKey with AOSP7.1 kernel 4.4.
> Patch v2->v3 changes:
> whitespace change.
> 
>   drivers/iio/light/rpr0521.c |   10 ++++++++--
>   1 file changed, 8 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
> index 7de0f39..03504f6 100644
> --- a/drivers/iio/light/rpr0521.c
> +++ b/drivers/iio/light/rpr0521.c
> @@ -197,7 +197,10 @@ static int rpr0521_als_enable(struct rpr0521_data *data, u8 status)
>   	if (ret < 0)
>   		return ret;
>   
> -	data->als_dev_en = true;
> +	if (status & RPR0521_MODE_ALS_MASK)
> +		data->als_dev_en = true;
> +	else
> +		data->als_dev_en = false;
>   
>   	return 0;
>   }
> @@ -212,7 +215,10 @@ static int rpr0521_pxs_enable(struct rpr0521_data *data, u8 status)
>   	if (ret < 0)
>   		return ret;
>   
> -	data->pxs_dev_en = true;
> +	if (status & RPR0521_MODE_PXS_MASK)
> +		data->pxs_dev_en = true;
> +	else
> +		data->pxs_dev_en = false;
>   
>   	return 0;
>   }
> 


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v5 2/9] iio: light: rpr0521 poweroff for probe fails
  2017-05-18 12:12 ` [PATCH v5 2/9] iio: light: rpr0521 poweroff for probe fails Mikko Koivunen
@ 2017-05-21 11:08   ` Jonathan Cameron
  0 siblings, 0 replies; 22+ messages in thread
From: Jonathan Cameron @ 2017-05-21 11:08 UTC (permalink / raw)
  To: Mikko Koivunen; +Cc: pmeerw, knaack.h, lars, Daniel Baluta, linux-iio

On 18/05/17 13:12, Mikko Koivunen wrote:
> Set sensor measurement off after probe fail in pm_runtime_set_active() or
> iio_device_register(). Without this change sensor measurement stays on
> even though probe fails on these calls.
> 
> This is maybe rare case, but causes constant power drain without any
> benefits when it happens. Power drain is 20-500uA, typically 180uA.
> 
> Signed-off-by: Mikko Koivunen <mikko.koivunen@fi.rohmeurope.com>
Applied to the togreg branch of iio.git and pushed out as testing
for the autobuilders to play with it.
Thanks,

Jonathan
> ---
> Sensor measurement is turned on rpr0521_init() so in probe it should be
> turned off in error cases after that. With CONFIG_PM it's probably
> unnecessary for iio_device_register()-fail, but without CONFIG_PM it is
> needed. Writing power off twice when CONFIG_PM enabled is ok.
> 
> Patch v4->v5 changes:
> errorpath (void) casting removed
> 
>   drivers/iio/light/rpr0521.c |   17 +++++++++++++++--
>   1 file changed, 15 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
> index 03504f6..84d8d40 100644
> --- a/drivers/iio/light/rpr0521.c
> +++ b/drivers/iio/light/rpr0521.c
> @@ -516,13 +516,26 @@ static int rpr0521_probe(struct i2c_client *client,
>   
>   	ret = pm_runtime_set_active(&client->dev);
>   	if (ret < 0)
> -		return ret;
> +		goto err_poweroff;
>   
>   	pm_runtime_enable(&client->dev);
>   	pm_runtime_set_autosuspend_delay(&client->dev, RPR0521_SLEEP_DELAY_MS);
>   	pm_runtime_use_autosuspend(&client->dev);
>   
> -	return iio_device_register(indio_dev);
> +	ret = iio_device_register(indio_dev);
> +	if (ret)
> +		goto err_pm_disable;
> +
> +	return 0;
> +
> +err_pm_disable:
> +	pm_runtime_disable(&client->dev);
> +	pm_runtime_set_suspended(&client->dev);
> +	pm_runtime_put_noidle(&client->dev);
> +err_poweroff:
> +	rpr0521_poweroff(data);
> +
> +	return ret;
>   }
>   
>   static int rpr0521_remove(struct i2c_client *client)
> 


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v5 3/9] iio: light: rpr0521 on-off sequence change for CONFIG_PM
  2017-05-18 12:12 ` [PATCH v5 3/9] iio: light: rpr0521 on-off sequence change for CONFIG_PM Mikko Koivunen
@ 2017-05-21 11:10   ` Jonathan Cameron
  0 siblings, 0 replies; 22+ messages in thread
From: Jonathan Cameron @ 2017-05-21 11:10 UTC (permalink / raw)
  To: Mikko Koivunen; +Cc: pmeerw, knaack.h, lars, Daniel Baluta, linux-iio

On 18/05/17 13:12, Mikko Koivunen wrote:
> Refactor _set_power_state(), _resume() and _suspend().
> Enable measurement only when needed, not in _init(). System can suspend
> during measurement and measurement is continued on resume.
> Pm turns off measurement when both ps and als measurements are disabled for
> 2 seconds. During off-time the power save is 20-500mA, typically 180mA.
> 
> Signed-off-by: Mikko Koivunen <mikko.koivunen@fi.rohmeurope.com>
Applied.
> ---
>   Don't enable measurement on _init() when using CONFIG_PM. Enable only
> when needed, otherwise it messes up the pm suspend-resume. Polling
> enables/disables the measurement anyway.
> 
> Save ALS/PS enabled status on _suspend() when called during measurement,
> so that measurement can be re-enabled on _resume().
> 
> checkpatch.pl OK
> Tested on LeMaker HiKey with AOSP7.1 kernel 4.4.
> Builds ok with torvalds/linux feb 27.
> 
>   drivers/iio/light/rpr0521.c |   70 ++++++++++++++++++++++++++++---------------
>   1 file changed, 46 insertions(+), 24 deletions(-)
> 
> diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
> index 84d8d40..9219819 100644
> --- a/drivers/iio/light/rpr0521.c
> +++ b/drivers/iio/light/rpr0521.c
> @@ -9,7 +9,7 @@
>    *
>    * IIO driver for RPR-0521RS (7-bit I2C slave address 0x38).
>    *
> - * TODO: illuminance channel, PM support, buffer
> + * TODO: illuminance channel, buffer
>    */
>   
>   #include <linux/module.h>
> @@ -142,9 +142,11 @@ struct rpr0521_data {
>   	bool als_dev_en;
>   	bool pxs_dev_en;
>   
> -	/* optimize runtime pm ops - enable device only if needed */
> +	/* optimize runtime pm ops - enable/disable device only if needed */
>   	bool als_ps_need_en;
>   	bool pxs_ps_need_en;
> +	bool als_need_dis;
> +	bool pxs_need_dis;
>   
>   	struct regmap *regmap;
>   };
> @@ -230,40 +232,32 @@ static int rpr0521_pxs_enable(struct rpr0521_data *data, u8 status)
>    * @on: state to be set for devices in @device_mask
>    * @device_mask: bitmask specifying for which device we need to update @on state
>    *
> - * We rely on rpr0521_runtime_resume to enable our @device_mask devices, but
> - * if (for example) PXS was enabled (pxs_dev_en = true) by a previous call to
> - * rpr0521_runtime_resume and we want to enable ALS we MUST set ALS enable
> - * bit of RPR0521_REG_MODE_CTRL here because rpr0521_runtime_resume will not
> - * be called twice.
> + * Calls for this function must be balanced so that each ON should have matching
> + * OFF. Otherwise pm usage_count gets out of sync.
>    */
>   static int rpr0521_set_power_state(struct rpr0521_data *data, bool on,
>   				   u8 device_mask)
>   {
>   #ifdef CONFIG_PM
>   	int ret;
> -	u8 update_mask = 0;
>   
>   	if (device_mask & RPR0521_MODE_ALS_MASK) {
> -		if (on && !data->als_ps_need_en && data->pxs_dev_en)
> -			update_mask |= RPR0521_MODE_ALS_MASK;
> -		else
> -			data->als_ps_need_en = on;
> +		data->als_ps_need_en = on;
> +		data->als_need_dis = !on;
>   	}
>   
>   	if (device_mask & RPR0521_MODE_PXS_MASK) {
> -		if (on && !data->pxs_ps_need_en && data->als_dev_en)
> -			update_mask |= RPR0521_MODE_PXS_MASK;
> -		else
> -			data->pxs_ps_need_en = on;
> -	}
> -
> -	if (update_mask) {
> -		ret = regmap_update_bits(data->regmap, RPR0521_REG_MODE_CTRL,
> -					 update_mask, update_mask);
> -		if (ret < 0)
> -			return ret;
> +		data->pxs_ps_need_en = on;
> +		data->pxs_need_dis = !on;
>   	}
>   
> +	/*
> +	 * On: _resume() is called only when we are suspended
> +	 * Off: _suspend() is called after delay if _resume() is not
> +	 * called before that.
> +	 * Note: If either measurement is re-enabled before _suspend(),
> +	 * both stay enabled until _suspend().
> +	 */
>   	if (on) {
>   		ret = pm_runtime_get_sync(&data->client->dev);
>   	} else {
> @@ -279,6 +273,23 @@ static int rpr0521_set_power_state(struct rpr0521_data *data, bool on,
>   
>   		return ret;
>   	}
> +
> +	if (on) {
> +		/* If _resume() was not called, enable measurement now. */
> +		if (data->als_ps_need_en) {
> +			ret = rpr0521_als_enable(data, RPR0521_MODE_ALS_ENABLE);
> +			if (ret)
> +				return ret;
> +			data->als_ps_need_en = false;
> +		}
> +
> +		if (data->pxs_ps_need_en) {
> +			ret = rpr0521_pxs_enable(data, RPR0521_MODE_PXS_ENABLE);
> +			if (ret)
> +				return ret;
> +			data->pxs_ps_need_en = false;
> +		}
> +	}
>   #endif
>   	return 0;
>   }
> @@ -425,12 +436,14 @@ static int rpr0521_init(struct rpr0521_data *data)
>   		return ret;
>   	}
>   
> +#ifndef CONFIG_PM
>   	ret = rpr0521_als_enable(data, RPR0521_MODE_ALS_ENABLE);
>   	if (ret < 0)
>   		return ret;
>   	ret = rpr0521_pxs_enable(data, RPR0521_MODE_PXS_ENABLE);
>   	if (ret < 0)
>   		return ret;
> +#endif
>   
>   	return 0;
>   }
> @@ -560,9 +573,16 @@ static int rpr0521_runtime_suspend(struct device *dev)
>   	struct rpr0521_data *data = iio_priv(indio_dev);
>   	int ret;
>   
> -	/* disable channels and sets {als,pxs}_dev_en to false */
>   	mutex_lock(&data->lock);
> +	/* If measurements are enabled, enable them on resume */
> +	if (!data->als_need_dis)
> +		data->als_ps_need_en = data->als_dev_en;
> +	if (!data->pxs_need_dis)
> +		data->pxs_ps_need_en = data->pxs_dev_en;
> +
> +	/* disable channels and sets {als,pxs}_dev_en to false */
>   	ret = rpr0521_poweroff(data);
> +	regcache_mark_dirty(data->regmap);
>   	mutex_unlock(&data->lock);
>   
>   	return ret;
> @@ -574,6 +594,7 @@ static int rpr0521_runtime_resume(struct device *dev)
>   	struct rpr0521_data *data = iio_priv(indio_dev);
>   	int ret;
>   
> +	regcache_sync(data->regmap);
>   	if (data->als_ps_need_en) {
>   		ret = rpr0521_als_enable(data, RPR0521_MODE_ALS_ENABLE);
>   		if (ret < 0)
> @@ -587,6 +608,7 @@ static int rpr0521_runtime_resume(struct device *dev)
>   			return ret;
>   		data->pxs_ps_need_en = false;
>   	}
> +	msleep(100);	//wait for first measurement result
>   
>   	return 0;
>   }
> 


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v5 4/9] iio: light: rpr0521 magic number to sizeof() on value read
  2017-05-18 12:12 ` [PATCH v5 4/9] iio: light: rpr0521 magic number to sizeof() on value read Mikko Koivunen
@ 2017-05-21 11:11   ` Jonathan Cameron
  0 siblings, 0 replies; 22+ messages in thread
From: Jonathan Cameron @ 2017-05-21 11:11 UTC (permalink / raw)
  To: Mikko Koivunen; +Cc: pmeerw, knaack.h, lars, Daniel Baluta, linux-iio

On 18/05/17 13:12, Mikko Koivunen wrote:
> Changed magic number to sizeof() on value read.
> 
> Signed-off-by: Mikko Koivunen <mikko.koivunen@fi.rohmeurope.com>
Applied.
> ---
> 
>   drivers/iio/light/rpr0521.c |    2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
> index 9219819..38095d7 100644
> --- a/drivers/iio/light/rpr0521.c
> +++ b/drivers/iio/light/rpr0521.c
> @@ -356,7 +356,7 @@ static int rpr0521_read_raw(struct iio_dev *indio_dev,
>   
>   		ret = regmap_bulk_read(data->regmap,
>   				       rpr0521_data_reg[chan->address].address,
> -				       &raw_data, 2);
> +				       &raw_data, sizeof(raw_data));
>   		if (ret < 0) {
>   			rpr0521_set_power_state(data, false, device_mask);
>   			mutex_unlock(&data->lock);
> 


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v5 5/9] iio: light: rpr0521 whitespace fixes
  2017-05-18 12:12 ` [PATCH v5 5/9] iio: light: rpr0521 whitespace fixes Mikko Koivunen
@ 2017-05-21 11:12   ` Jonathan Cameron
  0 siblings, 0 replies; 22+ messages in thread
From: Jonathan Cameron @ 2017-05-21 11:12 UTC (permalink / raw)
  To: Mikko Koivunen; +Cc: pmeerw, knaack.h, lars, Daniel Baluta, linux-iio

On 18/05/17 13:12, Mikko Koivunen wrote:
> Just whitespace change, no functional changes.
> 
> Signed-off-by: Mikko Koivunen <mikko.koivunen@fi.rohmeurope.com>
Applied.
> ---
> 
>   drivers/iio/light/rpr0521.c |    3 +++
>   1 file changed, 3 insertions(+)
> 
> diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
> index 38095d7..52c559d 100644
> --- a/drivers/iio/light/rpr0521.c
> +++ b/drivers/iio/light/rpr0521.c
> @@ -371,6 +371,7 @@ static int rpr0521_read_raw(struct iio_dev *indio_dev,
>   		*val = le16_to_cpu(raw_data);
>   
>   		return IIO_VAL_INT;
> +
>   	case IIO_CHAN_INFO_SCALE:
>   		mutex_lock(&data->lock);
>   		ret = rpr0521_get_gain(data, chan->address, val, val2);
> @@ -379,6 +380,7 @@ static int rpr0521_read_raw(struct iio_dev *indio_dev,
>   			return ret;
>   
>   		return IIO_VAL_INT_PLUS_MICRO;
> +
>   	default:
>   		return -EINVAL;
>   	}
> @@ -398,6 +400,7 @@ static int rpr0521_write_raw(struct iio_dev *indio_dev,
>   		mutex_unlock(&data->lock);
>   
>   		return ret;
> +
>   	default:
>   		return -EINVAL;
>   	}
> 


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v5 6/9] iio: light: rpr0521 sample_frequency read/write
  2017-05-18 12:12 ` [PATCH v5 6/9] iio: light: rpr0521 sample_frequency read/write Mikko Koivunen
@ 2017-05-21 11:14   ` Jonathan Cameron
  0 siblings, 0 replies; 22+ messages in thread
From: Jonathan Cameron @ 2017-05-21 11:14 UTC (permalink / raw)
  To: Mikko Koivunen; +Cc: pmeerw, knaack.h, lars, Daniel Baluta, linux-iio

On 18/05/17 13:12, Mikko Koivunen wrote:
> Add sysfs read/write sample frequency.
> 
> Signed-off-by: Mikko Koivunen <mikko.koivunen@fi.rohmeurope.com>
Applied.
> ---
> Patch v1->v2 changes:
> multiline comments fixed
> Patch v2->v3 changes:
> multiline comments fixed
> Empty line fixes on switch-case.
> Changed if-elseif-else to switch-case.
> checkpatch.pl OK
> Tested on LeMaker HiKey with AOSP7.1 kernel 4.4.
> Builds ok with torvalds/linux feb 27.
> 
> Patch v3->v4 changes:
> indent fix
> 
>   drivers/iio/light/rpr0521.c |  117 +++++++++++++++++++++++++++++++++++++++++++
>   1 file changed, 117 insertions(+)
> 
> diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
> index 52c559d..0aac4bc 100644
> --- a/drivers/iio/light/rpr0521.c
> +++ b/drivers/iio/light/rpr0521.c
> @@ -132,6 +132,30 @@ static const struct rpr0521_gain_info {
>   	},
>   };
>   
> +struct rpr0521_samp_freq {
> +	int	als_hz;
> +	int	als_uhz;
> +	int	pxs_hz;
> +	int	pxs_uhz;
> +};
> +
> +static const struct rpr0521_samp_freq rpr0521_samp_freq_i[13] = {
> +/*	{ALS, PXS},		   W==currently writable option */
> +	{0, 0, 0, 0},		/* W0000, 0=standby */
> +	{0, 0, 100, 0},		/*  0001 */
> +	{0, 0, 25, 0},		/*  0010 */
> +	{0, 0, 10, 0},		/*  0011 */
> +	{0, 0, 2, 500000},	/*  0100 */
> +	{10, 0, 20, 0},		/*  0101 */
> +	{10, 0, 10, 0},		/* W0110 */
> +	{10, 0, 2, 500000},	/*  0111 */
> +	{2, 500000, 20, 0},	/*  1000, measurement 100ms, sleep 300ms */
> +	{2, 500000, 10, 0},	/*  1001, measurement 100ms, sleep 300ms */
> +	{2, 500000, 0, 0},	/*  1010, high sensitivity mode */
> +	{2, 500000, 2, 500000},	/* W1011, high sensitivity mode */
> +	{20, 0, 20, 0}	/* 1100, ALS_data x 0.5, see specification P.18 */
> +};
> +
>   struct rpr0521_data {
>   	struct i2c_client *client;
>   
> @@ -154,9 +178,16 @@ struct rpr0521_data {
>   static IIO_CONST_ATTR(in_intensity_scale_available, RPR0521_ALS_SCALE_AVAIL);
>   static IIO_CONST_ATTR(in_proximity_scale_available, RPR0521_PXS_SCALE_AVAIL);
>   
> +/*
> + * Start with easy freq first, whole table of freq combinations is more
> + * complicated.
> + */
> +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("2.5 10");
> +
>   static struct attribute *rpr0521_attributes[] = {
>   	&iio_const_attr_in_intensity_scale_available.dev_attr.attr,
>   	&iio_const_attr_in_proximity_scale_available.dev_attr.attr,
> +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
>   	NULL,
>   };
>   
> @@ -172,6 +203,7 @@ static const struct iio_chan_spec rpr0521_channels[] = {
>   		.channel2 = IIO_MOD_LIGHT_BOTH,
>   		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>   			BIT(IIO_CHAN_INFO_SCALE),
> +		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
>   	},
>   	{
>   		.type = IIO_INTENSITY,
> @@ -180,12 +212,14 @@ static const struct iio_chan_spec rpr0521_channels[] = {
>   		.channel2 = IIO_MOD_LIGHT_IR,
>   		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>   			BIT(IIO_CHAN_INFO_SCALE),
> +		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
>   	},
>   	{
>   		.type = IIO_PROXIMITY,
>   		.address = RPR0521_CHAN_PXS,
>   		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>   			BIT(IIO_CHAN_INFO_SCALE),
> +		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
>   	}
>   };
>   
> @@ -331,6 +365,72 @@ static int rpr0521_set_gain(struct rpr0521_data *data, int chan,
>   				  idx << rpr0521_gain[chan].shift);
>   }
>   
> +static int rpr0521_read_samp_freq(struct rpr0521_data *data,
> +				enum iio_chan_type chan_type,
> +			    int *val, int *val2)
> +{
> +	int reg, ret;
> +
> +	ret = regmap_read(data->regmap, RPR0521_REG_MODE_CTRL, &reg);
> +	if (ret < 0)
> +		return ret;
> +
> +	reg &= RPR0521_MODE_MEAS_TIME_MASK;
> +	if (reg >= ARRAY_SIZE(rpr0521_samp_freq_i))
> +		return -EINVAL;
> +
> +	switch (chan_type) {
> +	case IIO_INTENSITY:
> +		*val = rpr0521_samp_freq_i[reg].als_hz;
> +		*val2 = rpr0521_samp_freq_i[reg].als_uhz;
> +		return 0;
> +
> +	case IIO_PROXIMITY:
> +		*val = rpr0521_samp_freq_i[reg].pxs_hz;
> +		*val2 = rpr0521_samp_freq_i[reg].pxs_uhz;
> +		return 0;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int rpr0521_write_samp_freq_common(struct rpr0521_data *data,
> +				enum iio_chan_type chan_type,
> +				int val, int val2)
> +{
> +	int i;
> +
> +	/*
> +	 * Ignore channel
> +	 * both pxs and als are setup only to same freq because of simplicity
> +	 */
> +	switch (val) {
> +	case 0:
> +		i = 0;
> +		break;
> +
> +	case 2:
> +		if (val2 != 500000)
> +			return -EINVAL;
> +
> +		i = 11;
> +		break;
> +
> +	case 10:
> +		i = 6;
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return regmap_update_bits(data->regmap,
> +		RPR0521_REG_MODE_CTRL,
> +		RPR0521_MODE_MEAS_TIME_MASK,
> +		i);
> +}
> +
>   static int rpr0521_read_raw(struct iio_dev *indio_dev,
>   			    struct iio_chan_spec const *chan, int *val,
>   			    int *val2, long mask)
> @@ -381,6 +481,15 @@ static int rpr0521_read_raw(struct iio_dev *indio_dev,
>   
>   		return IIO_VAL_INT_PLUS_MICRO;
>   
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		mutex_lock(&data->lock);
> +		ret = rpr0521_read_samp_freq(data, chan->type, val, val2);
> +		mutex_unlock(&data->lock);
> +		if (ret < 0)
> +			return ret;
> +
> +		return IIO_VAL_INT_PLUS_MICRO;
> +
>   	default:
>   		return -EINVAL;
>   	}
> @@ -401,6 +510,14 @@ static int rpr0521_write_raw(struct iio_dev *indio_dev,
>   
>   		return ret;
>   
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		mutex_lock(&data->lock);
> +		ret = rpr0521_write_samp_freq_common(data, chan->type,
> +						     val, val2);
> +		mutex_unlock(&data->lock);
> +
> +		return ret;
> +
>   	default:
>   		return -EINVAL;
>   	}
> 


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v5 7/9] iio: light: rpr0521 proximity offset read/write
  2017-05-18 12:12 ` [PATCH v5 7/9] iio: light: rpr0521 proximity offset read/write Mikko Koivunen
@ 2017-05-21 11:15   ` Jonathan Cameron
  0 siblings, 0 replies; 22+ messages in thread
From: Jonathan Cameron @ 2017-05-21 11:15 UTC (permalink / raw)
  To: Mikko Koivunen; +Cc: pmeerw, knaack.h, lars, Daniel Baluta, linux-iio

On 18/05/17 13:12, Mikko Koivunen wrote:
> Add sysfs read/write proximity offset feature. Offset is read/write from
> sensor registers. Values are proximity raw 10-bit values. After applying
> offset value, output values will be (measured_raw - offset_value). Output
> values are unsigned so offset value doesn't make output negative.
> 
> Signed-off-by: Mikko Koivunen <mikko.koivunen@fi.rohmeurope.com>
Applied.  Thanks
> ---
> Patch v1->v2 changes:
> read/write_ps_offset() rewritten with:
>   -  static, __le16, cpu_to_le16(), sizeof()
> Patch v2->v3 changes:
> RPR0521_*REG_*PS_OFFSET_LSB
> removed RPR0521_REG_PS_OFFSET_MSB
> Whitespace changes
> 
> checkpatch.pl OK
> Tested on LeMaker HiKey with AOSP7.1 kernel 4.4.
> Builds ok with torvalds/linux feb 27.
> 
>   drivers/iio/light/rpr0521.c |   52 +++++++++++++++++++++++++++++++++++++++++++
>   1 file changed, 52 insertions(+)
> 
> diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
> index 0aac4bc..83b24d1 100644
> --- a/drivers/iio/light/rpr0521.c
> +++ b/drivers/iio/light/rpr0521.c
> @@ -30,6 +30,7 @@
>   #define RPR0521_REG_PXS_DATA		0x44 /* 16-bit, little endian */
>   #define RPR0521_REG_ALS_DATA0		0x46 /* 16-bit, little endian */
>   #define RPR0521_REG_ALS_DATA1		0x48 /* 16-bit, little endian */
> +#define RPR0521_REG_PS_OFFSET_LSB	0x53
>   #define RPR0521_REG_ID			0x92
>   
>   #define RPR0521_MODE_ALS_MASK		BIT(7)
> @@ -218,6 +219,7 @@ static const struct iio_chan_spec rpr0521_channels[] = {
>   		.type = IIO_PROXIMITY,
>   		.address = RPR0521_CHAN_PXS,
>   		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +			BIT(IIO_CHAN_INFO_OFFSET) |
>   			BIT(IIO_CHAN_INFO_SCALE),
>   		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
>   	}
> @@ -431,6 +433,40 @@ static int rpr0521_write_samp_freq_common(struct rpr0521_data *data,
>   		i);
>   }
>   
> +static int rpr0521_read_ps_offset(struct rpr0521_data *data, int *offset)
> +{
> +	int ret;
> +	__le16 buffer;
> +
> +	ret = regmap_bulk_read(data->regmap,
> +		RPR0521_REG_PS_OFFSET_LSB, &buffer, sizeof(buffer));
> +
> +	if (ret < 0) {
> +		dev_err(&data->client->dev, "Failed to read PS OFFSET register\n");
> +		return ret;
> +	}
> +	*offset = le16_to_cpu(buffer);
> +
> +	return ret;
> +}
> +
> +static int rpr0521_write_ps_offset(struct rpr0521_data *data, int offset)
> +{
> +	int ret;
> +	__le16 buffer;
> +
> +	buffer = cpu_to_le16(offset & 0x3ff);
> +	ret = regmap_raw_write(data->regmap,
> +		RPR0521_REG_PS_OFFSET_LSB, &buffer, sizeof(buffer));
> +
> +	if (ret < 0) {
> +		dev_err(&data->client->dev, "Failed to write PS OFFSET register\n");
> +		return ret;
> +	}
> +
> +	return ret;
> +}
> +
>   static int rpr0521_read_raw(struct iio_dev *indio_dev,
>   			    struct iio_chan_spec const *chan, int *val,
>   			    int *val2, long mask)
> @@ -490,6 +526,15 @@ static int rpr0521_read_raw(struct iio_dev *indio_dev,
>   
>   		return IIO_VAL_INT_PLUS_MICRO;
>   
> +	case IIO_CHAN_INFO_OFFSET:
> +		mutex_lock(&data->lock);
> +		ret = rpr0521_read_ps_offset(data, val);
> +		mutex_unlock(&data->lock);
> +		if (ret < 0)
> +			return ret;
> +
> +		return IIO_VAL_INT;
> +
>   	default:
>   		return -EINVAL;
>   	}
> @@ -518,6 +563,13 @@ static int rpr0521_write_raw(struct iio_dev *indio_dev,
>   
>   		return ret;
>   
> +	case IIO_CHAN_INFO_OFFSET:
> +		mutex_lock(&data->lock);
> +		ret = rpr0521_write_ps_offset(data, val);
> +		mutex_unlock(&data->lock);
> +
> +		return ret;
> +
>   	default:
>   		return -EINVAL;
>   	}
> 


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v5 8/9] iio: light: rpr0521 channel numbers reordered
  2017-05-18 12:12 ` [PATCH v5 8/9] iio: light: rpr0521 channel numbers reordered Mikko Koivunen
@ 2017-05-21 11:16   ` Jonathan Cameron
  0 siblings, 0 replies; 22+ messages in thread
From: Jonathan Cameron @ 2017-05-21 11:16 UTC (permalink / raw)
  To: Mikko Koivunen; +Cc: pmeerw, knaack.h, lars, Daniel Baluta, linux-iio

On 18/05/17 13:12, Mikko Koivunen wrote:
> Move proximity channel from last to first in structs to avoid confusion
> later with buffered triggers. Proximity data output is first in rpr0521
> register map.
> 
> Signed-off-by: Mikko Koivunen <mikko.koivunen@fi.rohmeurope.com>
Applied.

Thanks,

Jonathan
> ---
> 
>   drivers/iio/light/rpr0521.c |   40 ++++++++++++++++++++--------------------
>   1 file changed, 20 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
> index 83b24d1..9d0c2e8 100644
> --- a/drivers/iio/light/rpr0521.c
> +++ b/drivers/iio/light/rpr0521.c
> @@ -78,9 +78,9 @@ static const struct rpr0521_gain rpr0521_pxs_gain[3] = {
>   };
>   
>   enum rpr0521_channel {
> +	RPR0521_CHAN_PXS,
>   	RPR0521_CHAN_ALS_DATA0,
>   	RPR0521_CHAN_ALS_DATA1,
> -	RPR0521_CHAN_PXS,
>   };
>   
>   struct rpr0521_reg_desc {
> @@ -89,6 +89,10 @@ struct rpr0521_reg_desc {
>   };
>   
>   static const struct rpr0521_reg_desc rpr0521_data_reg[] = {
> +	[RPR0521_CHAN_PXS]	= {
> +		.address	= RPR0521_REG_PXS_DATA,
> +		.device_mask	= RPR0521_MODE_PXS_MASK,
> +	},
>   	[RPR0521_CHAN_ALS_DATA0] = {
>   		.address	= RPR0521_REG_ALS_DATA0,
>   		.device_mask	= RPR0521_MODE_ALS_MASK,
> @@ -97,10 +101,6 @@ static const struct rpr0521_reg_desc rpr0521_data_reg[] = {
>   		.address	= RPR0521_REG_ALS_DATA1,
>   		.device_mask	= RPR0521_MODE_ALS_MASK,
>   	},
> -	[RPR0521_CHAN_PXS]	= {
> -		.address	= RPR0521_REG_PXS_DATA,
> -		.device_mask	= RPR0521_MODE_PXS_MASK,
> -	},
>   };
>   
>   static const struct rpr0521_gain_info {
> @@ -110,6 +110,13 @@ static const struct rpr0521_gain_info {
>   	const struct rpr0521_gain *gain;
>   	int size;
>   } rpr0521_gain[] = {
> +	[RPR0521_CHAN_PXS] = {
> +		.reg	= RPR0521_REG_PXS_CTRL,
> +		.mask	= RPR0521_PXS_GAIN_MASK,
> +		.shift	= RPR0521_PXS_GAIN_SHIFT,
> +		.gain	= rpr0521_pxs_gain,
> +		.size	= ARRAY_SIZE(rpr0521_pxs_gain),
> +	},
>   	[RPR0521_CHAN_ALS_DATA0] = {
>   		.reg	= RPR0521_REG_ALS_CTRL,
>   		.mask	= RPR0521_ALS_DATA0_GAIN_MASK,
> @@ -124,13 +131,6 @@ static const struct rpr0521_gain_info {
>   		.gain	= rpr0521_als_gain,
>   		.size	= ARRAY_SIZE(rpr0521_als_gain),
>   	},
> -	[RPR0521_CHAN_PXS] = {
> -		.reg	= RPR0521_REG_PXS_CTRL,
> -		.mask	= RPR0521_PXS_GAIN_MASK,
> -		.shift	= RPR0521_PXS_GAIN_SHIFT,
> -		.gain	= rpr0521_pxs_gain,
> -		.size	= ARRAY_SIZE(rpr0521_pxs_gain),
> -	},
>   };
>   
>   struct rpr0521_samp_freq {
> @@ -198,6 +198,14 @@ static const struct attribute_group rpr0521_attribute_group = {
>   
>   static const struct iio_chan_spec rpr0521_channels[] = {
>   	{
> +		.type = IIO_PROXIMITY,
> +		.address = RPR0521_CHAN_PXS,
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +			BIT(IIO_CHAN_INFO_OFFSET) |
> +			BIT(IIO_CHAN_INFO_SCALE),
> +		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
> +	},
> +	{
>   		.type = IIO_INTENSITY,
>   		.modified = 1,
>   		.address = RPR0521_CHAN_ALS_DATA0,
> @@ -215,14 +223,6 @@ static const struct iio_chan_spec rpr0521_channels[] = {
>   			BIT(IIO_CHAN_INFO_SCALE),
>   		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
>   	},
> -	{
> -		.type = IIO_PROXIMITY,
> -		.address = RPR0521_CHAN_PXS,
> -		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> -			BIT(IIO_CHAN_INFO_OFFSET) |
> -			BIT(IIO_CHAN_INFO_SCALE),
> -		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
> -	}
>   };
>   
>   static int rpr0521_als_enable(struct rpr0521_data *data, u8 status)
> 


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v5 9/9] iio: light: rpr0521 triggered buffer
  2017-05-18 12:12 ` [PATCH v5 9/9] iio: light: rpr0521 triggered buffer Mikko Koivunen
@ 2017-05-21 11:34   ` Jonathan Cameron
  2017-05-25 13:53     ` Koivunen, Mikko
  0 siblings, 1 reply; 22+ messages in thread
From: Jonathan Cameron @ 2017-05-21 11:34 UTC (permalink / raw)
  To: Mikko Koivunen; +Cc: pmeerw, knaack.h, lars, Daniel Baluta, linux-iio

On 18/05/17 13:12, Mikko Koivunen wrote:
> Set up and use triggered buffer if there is irq defined for device in
> device tree. Trigger producer triggers from rpr0521 drdy interrupt line.
> Trigger consumer reads rpr0521 data to scan buffer.
> Depends on previous commits of _scale and _offset.
> 
> Signed-off-by: Mikko Koivunen <mikko.koivunen@fi.rohmeurope.com>
Hi Mikko,

A few last bits in the final patch of the series.  See inline.
The IRQ_NONE one might trigger some nasty issues and ultimately
disable the interrupt. However, it is the right option if we know
it's not our interrupt...

Jonathan
> ---
> Patch v2->v3 changes:
> .shift = 0 removed
> reordered functions to remove forward declarations
> whitespace changes
> refactored some update_bits->write, no "err = err || *"-pattern
> refactored trigger_consumer_handler
> reordered _probe() and _remove()
> added int clear on poweroff()
> checkpatch.pl OK
> Tested on LeMaker HiKey with AOSP7.1 kernel 4.4.
> Builds ok with torvalds/linux feb 27.
> 
> Patch v3->v4 changes:
> Moved sensor on/off commands to buffer preenable and postdisable
>   - Since drdy happens only on measurement data ready and register writes
>     are cached, the trigger producer doesn't care of suspend/resume state.
> available_scan_masks added
> indent fix
> checkpatch.pl OK
> Lightly re-tested on LeMaker HiKey with AOSP7.1 kernel 4.4.
> Builds ok with torvalds/linux feb 27.
> 
>   drivers/iio/light/rpr0521.c |  294 ++++++++++++++++++++++++++++++++++++++++++-
>   1 file changed, 291 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
> index 9d0c2e8..1ef6060 100644
> --- a/drivers/iio/light/rpr0521.c
> +++ b/drivers/iio/light/rpr0521.c
> @@ -9,7 +9,7 @@
>    *
>    * IIO driver for RPR-0521RS (7-bit I2C slave address 0x38).
>    *
> - * TODO: illuminance channel, buffer
> + * TODO: illuminance channel
>    */
>   
>   #include <linux/module.h>
> @@ -20,6 +20,10 @@
>   #include <linux/acpi.h>
>   
>   #include <linux/iio/iio.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
>   #include <linux/iio/sysfs.h>
>   #include <linux/pm_runtime.h>
>   
> @@ -30,6 +34,7 @@
>   #define RPR0521_REG_PXS_DATA		0x44 /* 16-bit, little endian */
>   #define RPR0521_REG_ALS_DATA0		0x46 /* 16-bit, little endian */
>   #define RPR0521_REG_ALS_DATA1		0x48 /* 16-bit, little endian */
> +#define RPR0521_REG_INTERRUPT		0x4A
>   #define RPR0521_REG_PS_OFFSET_LSB	0x53
>   #define RPR0521_REG_ID			0x92
>   
> @@ -42,16 +47,29 @@
>   #define RPR0521_ALS_DATA1_GAIN_SHIFT	2
>   #define RPR0521_PXS_GAIN_MASK		GENMASK(5, 4)
>   #define RPR0521_PXS_GAIN_SHIFT		4
> +#define RPR0521_PXS_PERSISTENCE_MASK	GENMASK(3, 0)
> +#define RPR0521_INTERRUPT_INT_TRIG_PS_MASK	BIT(0)
> +#define RPR0521_INTERRUPT_INT_TRIG_ALS_MASK	BIT(1)
> +#define RPR0521_INTERRUPT_INT_REASSERT_MASK	BIT(3)
>   
>   #define RPR0521_MODE_ALS_ENABLE		BIT(7)
>   #define RPR0521_MODE_ALS_DISABLE	0x00
>   #define RPR0521_MODE_PXS_ENABLE		BIT(6)
>   #define RPR0521_MODE_PXS_DISABLE	0x00
> +#define RPR0521_PXS_PERSISTENCE_DRDY	0x00
> +
> +#define RPR0521_INTERRUPT_INT_TRIG_PS_ENABLE	BIT(0)
> +#define RPR0521_INTERRUPT_INT_TRIG_PS_DISABLE	0x00
> +#define RPR0521_INTERRUPT_INT_TRIG_ALS_ENABLE	BIT(1)
> +#define RPR0521_INTERRUPT_INT_TRIG_ALS_DISABLE	0x00
> +#define RPR0521_INTERRUPT_INT_REASSERT_ENABLE	BIT(3)
> +#define RPR0521_INTERRUPT_INT_REASSERT_DISABLE	0x00
>   
>   #define RPR0521_MANUFACT_ID		0xE0
>   #define RPR0521_DEFAULT_MEAS_TIME	0x06 /* ALS - 100ms, PXS - 100ms */
>   
>   #define RPR0521_DRV_NAME		"RPR0521"
> +#define RPR0521_IRQ_NAME		"rpr0521_event"
>   #define RPR0521_REGMAP_NAME		"rpr0521_regmap"
>   
>   #define RPR0521_SLEEP_DELAY_MS	2000
> @@ -167,6 +185,9 @@ struct rpr0521_data {
>   	bool als_dev_en;
>   	bool pxs_dev_en;
>   
> +	struct iio_trigger *drdy_trigger0;
> +	bool drdy_trigger_on;
> +
>   	/* optimize runtime pm ops - enable/disable device only if needed */
>   	bool als_ps_need_en;
>   	bool pxs_ps_need_en;
> @@ -196,6 +217,19 @@ static const struct attribute_group rpr0521_attribute_group = {
>   	.attrs = rpr0521_attributes,
>   };
>   
> +/* Order of the channel data in buffer */
> +enum rpr0521_scan_index_order {
> +	RPR0521_CHAN_INDEX_PXS,
> +	RPR0521_CHAN_INDEX_BOTH,
> +	RPR0521_CHAN_INDEX_IR,
> +};
> +
> +static const unsigned long rpr0521_available_scan_masks[] = {
> +	BIT(RPR0521_CHAN_INDEX_PXS) | BIT(RPR0521_CHAN_INDEX_BOTH) |
> +	BIT(RPR0521_CHAN_INDEX_IR),
> +	0
> +};
> +
>   static const struct iio_chan_spec rpr0521_channels[] = {
>   	{
>   		.type = IIO_PROXIMITY,
> @@ -204,6 +238,13 @@ static const struct iio_chan_spec rpr0521_channels[] = {
>   			BIT(IIO_CHAN_INFO_OFFSET) |
>   			BIT(IIO_CHAN_INFO_SCALE),
>   		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
> +		.scan_index = RPR0521_CHAN_INDEX_PXS,
> +		.scan_type = {
> +			.sign = 'u',
> +			.realbits = 16,
> +			.storagebits = 16,
> +			.endianness = IIO_LE,
> +		},
>   	},
>   	{
>   		.type = IIO_INTENSITY,
> @@ -213,6 +254,13 @@ static const struct iio_chan_spec rpr0521_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 = RPR0521_CHAN_INDEX_BOTH,
> +		.scan_type = {
> +			.sign = 'u',
> +			.realbits = 16,
> +			.storagebits = 16,
> +			.endianness = IIO_LE,
> +		},
>   	},
>   	{
>   		.type = IIO_INTENSITY,
> @@ -222,6 +270,13 @@ static const struct iio_chan_spec rpr0521_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 = RPR0521_CHAN_INDEX_IR,
> +		.scan_type = {
> +			.sign = 'u',
> +			.realbits = 16,
> +			.storagebits = 16,
> +			.endianness = IIO_LE,
> +		},
>   	},
>   };
>   
> @@ -330,6 +385,150 @@ static int rpr0521_set_power_state(struct rpr0521_data *data, bool on,
>   	return 0;
>   }
>   
> +/* IRQ to trigger handler */
> +static irqreturn_t rpr0521_drdy_irq_handler(int irq, void *private)
> +{
> +	struct iio_dev *indio_dev = private;
> +	struct rpr0521_data *data = iio_priv(indio_dev);
> +
> +	/*
> +	 * Sometimes static on floating (hi-z) interrupt line causes interrupt.
> +	 * Notify trigger0 consumers/subscribers only if trigger has been
> +	 * enabled. This is to prevent i2c writes to sensor which is actually
> +	 * powered off.
> +	 */
> +	if (data->drdy_trigger_on)
> +		iio_trigger_poll(data->drdy_trigger0);
> +
If this isn't our interrupt we should really be returning IRQ_NONE
rather than IRQ_HANDLED.  That will trigger unhandled interrupt
messages etc in the log...
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t rpr0521_trigger_consumer_handler(int irq, void *p)
> +{
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *indio_dev = pf->indio_dev;
> +	struct rpr0521_data *data = iio_priv(indio_dev);
> +	int err;
> +
> +	u8 buffer[16]; /* 3 16-bit channels + padding + ts */
> +
> +	err = regmap_bulk_read(data->regmap, RPR0521_REG_PXS_DATA,
> +		&buffer,
> +		(3 * 2) + 1);	/* 3 * 16-bit + (discarded) int clear reg. */
> +	if (!err)
> +		iio_push_to_buffers_with_timestamp(indio_dev,
> +						   buffer, pf->timestamp);
> +	else
> +		dev_err(&data->client->dev,
> +			"Trigger consumer can't read from sensor.\n");
> +
> +	iio_trigger_notify_done(indio_dev->trig);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int rpr0521_write_int_enable(struct rpr0521_data *data)
> +{
> +	int err;
> +
> +	/* Interrupt after each measurement */
> +	err = regmap_update_bits(data->regmap, RPR0521_REG_PXS_CTRL,
> +		RPR0521_PXS_PERSISTENCE_MASK,
> +		RPR0521_PXS_PERSISTENCE_DRDY);
> +	if (err) {
> +		dev_err(&data->client->dev, "PS control reg write fail.\n");
> +		return -EBUSY;
> +		}
> +
> +	/* Ignore latch and mode because of drdy */
> +	err = regmap_write(data->regmap, RPR0521_REG_INTERRUPT,
> +		RPR0521_INTERRUPT_INT_REASSERT_DISABLE |
> +		RPR0521_INTERRUPT_INT_TRIG_ALS_DISABLE |
> +		RPR0521_INTERRUPT_INT_TRIG_PS_ENABLE
> +		);
> +	if (err) {
> +		dev_err(&data->client->dev, "Interrupt setup write fail.\n");
> +		return -EBUSY;
> +		}
> +
> +	return 0;
> +}
> +
> +static int rpr0521_write_int_disable(struct rpr0521_data *data)
> +{
> +	/* Don't care of clearing mode, assert and latch. */
> +	return regmap_write(data->regmap, RPR0521_REG_INTERRUPT,
> +				RPR0521_INTERRUPT_INT_TRIG_ALS_DISABLE |
> +				RPR0521_INTERRUPT_INT_TRIG_PS_DISABLE
> +				);
> +}
> +
> +/*
> + * Trigger producer enable / disable. Note that there will be trigs only when
> + * measurement data is ready to be read.
> + */
> +static int rpr0521_pxs_drdy_set_state(struct iio_trigger *trigger,
> +	bool enable_drdy)
> +{
> +	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trigger);
> +	struct rpr0521_data *data = iio_priv(indio_dev);
> +	int err;
> +
> +	if (enable_drdy)
> +		err = rpr0521_write_int_enable(data);
> +	else
> +		err = rpr0521_write_int_disable(data);
> +	if (err)
> +		dev_err(&data->client->dev, "rpr0521_pxs_drdy_set_state failed\n");
> +	else
> +		data->drdy_trigger_on = enable_drdy;
> +
> +	return err;
> +}
> +
> +static const struct iio_trigger_ops rpr0521_trigger_ops = {
> +	.set_trigger_state = rpr0521_pxs_drdy_set_state,
> +	.owner = THIS_MODULE,
> +	};
> +
> +
> +static int rpr0521_buffer_preenable(struct iio_dev *indio_dev)
> +{
> +	int err;
> +	struct rpr0521_data *data = iio_priv(indio_dev);
> +
> +	mutex_lock(&data->lock);
> +	err = rpr0521_set_power_state(data, true,
> +		(RPR0521_MODE_PXS_MASK | RPR0521_MODE_ALS_MASK));
> +	mutex_unlock(&data->lock);
> +	if (err)
> +		dev_err(&data->client->dev, "_buffer_preenable fail\n");
> +
> +	return err;
> +}
> +
> +static int rpr0521_buffer_postdisable(struct iio_dev *indio_dev)
> +{
> +	int err;
> +	struct rpr0521_data *data = iio_priv(indio_dev);
> +
> +	mutex_lock(&data->lock);
> +	err = rpr0521_set_power_state(data, false,
> +		(RPR0521_MODE_PXS_MASK | RPR0521_MODE_ALS_MASK));
> +	mutex_unlock(&data->lock);
> +	if (err)
> +		dev_err(&data->client->dev, "_buffer_postdisable fail\n");
> +
> +	return err;
> +}
> +
> +static const struct iio_buffer_setup_ops rpr0521_buffer_setup_ops = {
> +	.preenable = rpr0521_buffer_preenable,
> +	.postenable = iio_triggered_buffer_postenable,
> +	.predisable = iio_triggered_buffer_predisable,
> +	.postdisable = rpr0521_buffer_postdisable,
> +};
> +
>   static int rpr0521_get_gain(struct rpr0521_data *data, int chan,
>   			    int *val, int *val2)
>   {
> @@ -481,6 +680,9 @@ static int rpr0521_read_raw(struct iio_dev *indio_dev,
>   		if (chan->type != IIO_INTENSITY && chan->type != IIO_PROXIMITY)
>   			return -EINVAL;
>   
> +		if (iio_buffer_enabled(indio_dev))
> +			return -EBUSY;
This is racy.  You need to actually prevent a transition to buffered reading.
That means you want to use iio_claim_direct etc to hold the lock over state
transitions whilst the read is going on.

(sorry I missed this in earlier versions!)
> +
>   		device_mask = rpr0521_data_reg[chan->address].device_mask;
>   
>   		mutex_lock(&data->lock);
> @@ -623,6 +825,7 @@ static int rpr0521_init(struct rpr0521_data *data)
>   static int rpr0521_poweroff(struct rpr0521_data *data)
>   {
>   	int ret;
> +	int tmp;
>   
>   	ret = regmap_update_bits(data->regmap, RPR0521_REG_MODE_CTRL,
>   				 RPR0521_MODE_ALS_MASK |
> @@ -635,6 +838,16 @@ static int rpr0521_poweroff(struct rpr0521_data *data)
>   	data->als_dev_en = false;
>   	data->pxs_dev_en = false;
>   
> +	/*
> +	 * Int pin keeps state after power off. Set pin to high impedance
> +	 * mode to prevent power drain.
> +	 */
> +	ret = regmap_read(data->regmap, RPR0521_REG_INTERRUPT, &tmp);
> +	if (ret) {
> +		dev_err(&data->client->dev, "Failed to reset int pin.\n");
> +		return ret;
> +	}
> +
>   	return 0;
>   }
>   
> @@ -707,12 +920,78 @@ static int rpr0521_probe(struct i2c_client *client,
>   	pm_runtime_set_autosuspend_delay(&client->dev, RPR0521_SLEEP_DELAY_MS);
>   	pm_runtime_use_autosuspend(&client->dev);
>   
> +	/*
> +	 * If sensor write/read  is needed in _probe after _use_autosuspend,
> +	 * sensor needs to be _resumed first using rpr0521_set_power_state().
> +	 */
> +
> +	/* IRQ to trigger setup */
> +	if (client->irq) {
> +		/* Trigger0 producer setup */
> +		data->drdy_trigger0 = devm_iio_trigger_alloc(
> +			indio_dev->dev.parent,
> +			"%s-dev%d", indio_dev->name, indio_dev->id);
> +		if (!data->drdy_trigger0) {
> +			ret = -ENOMEM;
> +			goto err_pm_disable;
> +		}
> +		data->drdy_trigger0->dev.parent = indio_dev->dev.parent;
> +		data->drdy_trigger0->ops = &rpr0521_trigger_ops;
> +		indio_dev->available_scan_masks = rpr0521_available_scan_masks;
> +		iio_trigger_set_drvdata(data->drdy_trigger0, indio_dev);
> +
> +		/* Ties irq to trigger producer handler. */
> +		ret = devm_request_threaded_irq(&client->dev, client->irq,
> +			rpr0521_drdy_irq_handler,
> +			NULL,
> +			IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
> +			RPR0521_IRQ_NAME,
> +			indio_dev);
> +		if (ret < 0) {
> +			dev_err(&client->dev, "request irq %d for trigger0 failed\n",
> +				client->irq);
> +			goto err_pm_disable;
> +			}
> +
> +		ret = iio_trigger_register(data->drdy_trigger0);
> +		if (ret) {
> +			dev_err(&client->dev, "iio trigger register failed\n");
> +			goto err_pm_disable;
> +		}
> +
> +		/*
> +		 * Now whole pipe from physical interrupt (irq defined by
> +		 * devicetree to device) to trigger0 output is set up.
> +		 */
> +
> +		/* Trigger consumer setup */
> +		ret = iio_triggered_buffer_setup(indio_dev,
> +			&iio_pollfunc_store_time,
> +			rpr0521_trigger_consumer_handler,
> +			&rpr0521_buffer_setup_ops);
> +		if (ret < 0) {
> +			dev_err(&client->dev, "iio triggered buffer setup failed\n");
> +			/* Count on devm to free_irq */
> +			goto err_iio_trigger_unregister;
> +		}
> +	}
> +
>   	ret = iio_device_register(indio_dev);
>   	if (ret)
> -		goto err_pm_disable;
> +		goto err_iio_triggered_buffer_cleanup;
> +
> +	if (client->irq) {
> +		/* Set trigger0 as current trigger (and inc refcount) */
> +		indio_dev->trig = iio_trigger_get(data->drdy_trigger0);
> +	}
>   
>   	return 0;
>   
> +err_iio_triggered_buffer_cleanup:
> +	iio_triggered_buffer_cleanup(indio_dev);
> +err_iio_trigger_unregister:
> +	if (data->drdy_trigger0)
> +		iio_trigger_unregister(data->drdy_trigger0);
>   err_pm_disable:
>   	pm_runtime_disable(&client->dev);
>   	pm_runtime_set_suspended(&client->dev);
> @@ -726,14 +1005,23 @@ err_poweroff:
>   static int rpr0521_remove(struct i2c_client *client)
>   {
>   	struct iio_dev *indio_dev = i2c_get_clientdata(client);
> +	struct rpr0521_data *data = iio_priv(indio_dev);
> +
> +	if (indio_dev->trig)
> +		iio_trigger_put(indio_dev->trig);
There is not guarantee this is the same trigger as that
provided by this driver.  So strangely the right option here
is to get it as you have done which will set up the default
to be sane, but to allow userspace to be responsible for the
put by clearing current_trigger if it wants to remove the
driver.  As a general rule I'm fairly anti default triggers
but I do appreciate they are sometimes all that makes sense...
However, if that is the case, then you should have the various
validation callbacks provided to ensure this trigger is used
only for this device and the device can only use this trigger.

You 'could' set indio_dev->trig_readonly which will block
out userspace changes...  Perhaps that's what you want to
do here.
>   
>   	iio_device_unregister(indio_dev);
>   
> +	iio_triggered_buffer_cleanup(indio_dev);
> +
> +	if (data->drdy_trigger0)
> +		iio_trigger_unregister(data->drdy_trigger0);
> +
>   	pm_runtime_disable(&client->dev);
>   	pm_runtime_set_suspended(&client->dev);
>   	pm_runtime_put_noidle(&client->dev);
>   
> -	rpr0521_poweroff(iio_priv(indio_dev));
> +	rpr0521_poweroff(data);
>   
>   	return 0;
>   }
> 


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v5 9/9] iio: light: rpr0521 triggered buffer
  2017-05-21 11:34   ` Jonathan Cameron
@ 2017-05-25 13:53     ` Koivunen, Mikko
  2017-05-28 15:06       ` Jonathan Cameron
  0 siblings, 1 reply; 22+ messages in thread
From: Koivunen, Mikko @ 2017-05-25 13:53 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: pmeerw, knaack.h, lars, Daniel Baluta, linux-iio

On 21.5.2017 14:34, Jonathan Cameron wrote:=0A=
> On 18/05/17 13:12, Mikko Koivunen wrote:=0A=
>> Set up and use triggered buffer if there is irq defined for device in=0A=
>> device tree. Trigger producer triggers from rpr0521 drdy interrupt line.=
=0A=
>> Trigger consumer reads rpr0521 data to scan buffer.=0A=
>> Depends on previous commits of _scale and _offset.=0A=
>>=0A=
>> Signed-off-by: Mikko Koivunen <mikko.koivunen@fi.rohmeurope.com>=0A=
> Hi Mikko,=0A=
>=0A=
> A few last bits in the final patch of the series.  See inline.=0A=
> The IRQ_NONE one might trigger some nasty issues and ultimately=0A=
> disable the interrupt. =0A=
=0A=
Do you have any pointer for more information about that?=0A=
=0A=
> However, it is the right option if we know=0A=
> it's not our interrupt...=0A=
=0A=
Ok, agree. It's also mandatory for shared interrupt -> changing.=0A=
=0A=
> Jonathan=0A=
>> ---=0A=
>> Patch v2->v3 changes:=0A=
>> .shift =3D 0 removed=0A=
>> reordered functions to remove forward declarations=0A=
>> whitespace changes=0A=
>> refactored some update_bits->write, no "err =3D err || *"-pattern=0A=
>> refactored trigger_consumer_handler=0A=
>> reordered _probe() and _remove()=0A=
>> added int clear on poweroff()=0A=
>> checkpatch.pl OK=0A=
>> Tested on LeMaker HiKey with AOSP7.1 kernel 4.4.=0A=
>> Builds ok with torvalds/linux feb 27.=0A=
>>=0A=
>> Patch v3->v4 changes:=0A=
>> Moved sensor on/off commands to buffer preenable and postdisable=0A=
>>   - Since drdy happens only on measurement data ready and register write=
s=0A=
>>     are cached, the trigger producer doesn't care of suspend/resume stat=
e.=0A=
>> available_scan_masks added=0A=
>> indent fix=0A=
>> checkpatch.pl OK=0A=
>> Lightly re-tested on LeMaker HiKey with AOSP7.1 kernel 4.4.=0A=
>> Builds ok with torvalds/linux feb 27.=0A=
>>=0A=
>>   drivers/iio/light/rpr0521.c |  294 +++++++++++++++++++++++++++++++++++=
+++++++-=0A=
>>   1 file changed, 291 insertions(+), 3 deletions(-)=0A=
>>=0A=
>> diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c=
=0A=
>> index 9d0c2e8..1ef6060 100644=0A=
>> --- a/drivers/iio/light/rpr0521.c=0A=
>> +++ b/drivers/iio/light/rpr0521.c=0A=
>> @@ -9,7 +9,7 @@=0A=
>>    *=0A=
>>    * IIO driver for RPR-0521RS (7-bit I2C slave address 0x38).=0A=
>>    *=0A=
>> - * TODO: illuminance channel, buffer=0A=
>> + * TODO: illuminance channel=0A=
>>    */=0A=
>>   =0A=
>>   #include <linux/module.h>=0A=
>> @@ -20,6 +20,10 @@=0A=
>>   #include <linux/acpi.h>=0A=
>>   =0A=
>>   #include <linux/iio/iio.h>=0A=
>> +#include <linux/iio/buffer.h>=0A=
>> +#include <linux/iio/trigger.h>=0A=
>> +#include <linux/iio/trigger_consumer.h>=0A=
>> +#include <linux/iio/triggered_buffer.h>=0A=
>>   #include <linux/iio/sysfs.h>=0A=
>>   #include <linux/pm_runtime.h>=0A=
>>   =0A=
>> @@ -30,6 +34,7 @@=0A=
>>   #define RPR0521_REG_PXS_DATA		0x44 /* 16-bit, little endian */=0A=
>>   #define RPR0521_REG_ALS_DATA0		0x46 /* 16-bit, little endian */=0A=
>>   #define RPR0521_REG_ALS_DATA1		0x48 /* 16-bit, little endian */=0A=
>> +#define RPR0521_REG_INTERRUPT		0x4A=0A=
>>   #define RPR0521_REG_PS_OFFSET_LSB	0x53=0A=
>>   #define RPR0521_REG_ID			0x92=0A=
>>   =0A=
>> @@ -42,16 +47,29 @@=0A=
>>   #define RPR0521_ALS_DATA1_GAIN_SHIFT	2=0A=
>>   #define RPR0521_PXS_GAIN_MASK		GENMASK(5, 4)=0A=
>>   #define RPR0521_PXS_GAIN_SHIFT		4=0A=
>> +#define RPR0521_PXS_PERSISTENCE_MASK	GENMASK(3, 0)=0A=
>> +#define RPR0521_INTERRUPT_INT_TRIG_PS_MASK	BIT(0)=0A=
>> +#define RPR0521_INTERRUPT_INT_TRIG_ALS_MASK	BIT(1)=0A=
>> +#define RPR0521_INTERRUPT_INT_REASSERT_MASK	BIT(3)=0A=
>>   =0A=
>>   #define RPR0521_MODE_ALS_ENABLE		BIT(7)=0A=
>>   #define RPR0521_MODE_ALS_DISABLE	0x00=0A=
>>   #define RPR0521_MODE_PXS_ENABLE		BIT(6)=0A=
>>   #define RPR0521_MODE_PXS_DISABLE	0x00=0A=
>> +#define RPR0521_PXS_PERSISTENCE_DRDY	0x00=0A=
>> +=0A=
>> +#define RPR0521_INTERRUPT_INT_TRIG_PS_ENABLE	BIT(0)=0A=
>> +#define RPR0521_INTERRUPT_INT_TRIG_PS_DISABLE	0x00=0A=
>> +#define RPR0521_INTERRUPT_INT_TRIG_ALS_ENABLE	BIT(1)=0A=
>> +#define RPR0521_INTERRUPT_INT_TRIG_ALS_DISABLE	0x00=0A=
>> +#define RPR0521_INTERRUPT_INT_REASSERT_ENABLE	BIT(3)=0A=
>> +#define RPR0521_INTERRUPT_INT_REASSERT_DISABLE	0x00=0A=
>>   =0A=
>>   #define RPR0521_MANUFACT_ID		0xE0=0A=
>>   #define RPR0521_DEFAULT_MEAS_TIME	0x06 /* ALS - 100ms, PXS - 100ms */=
=0A=
>>   =0A=
>>   #define RPR0521_DRV_NAME		"RPR0521"=0A=
>> +#define RPR0521_IRQ_NAME		"rpr0521_event"=0A=
>>   #define RPR0521_REGMAP_NAME		"rpr0521_regmap"=0A=
>>   =0A=
>>   #define RPR0521_SLEEP_DELAY_MS	2000=0A=
>> @@ -167,6 +185,9 @@ struct rpr0521_data {=0A=
>>   	bool als_dev_en;=0A=
>>   	bool pxs_dev_en;=0A=
>>   =0A=
>> +	struct iio_trigger *drdy_trigger0;=0A=
>> +	bool drdy_trigger_on;=0A=
>> +=0A=
>>   	/* optimize runtime pm ops - enable/disable device only if needed */=
=0A=
>>   	bool als_ps_need_en;=0A=
>>   	bool pxs_ps_need_en;=0A=
>> @@ -196,6 +217,19 @@ static const struct attribute_group rpr0521_attribu=
te_group =3D {=0A=
>>   	.attrs =3D rpr0521_attributes,=0A=
>>   };=0A=
>>   =0A=
>> +/* Order of the channel data in buffer */=0A=
>> +enum rpr0521_scan_index_order {=0A=
>> +	RPR0521_CHAN_INDEX_PXS,=0A=
>> +	RPR0521_CHAN_INDEX_BOTH,=0A=
>> +	RPR0521_CHAN_INDEX_IR,=0A=
>> +};=0A=
>> +=0A=
>> +static const unsigned long rpr0521_available_scan_masks[] =3D {=0A=
>> +	BIT(RPR0521_CHAN_INDEX_PXS) | BIT(RPR0521_CHAN_INDEX_BOTH) |=0A=
>> +	BIT(RPR0521_CHAN_INDEX_IR),=0A=
>> +	0=0A=
>> +};=0A=
>> +=0A=
>>   static const struct iio_chan_spec rpr0521_channels[] =3D {=0A=
>>   	{=0A=
>>   		.type =3D IIO_PROXIMITY,=0A=
>> @@ -204,6 +238,13 @@ static const struct iio_chan_spec rpr0521_channels[=
] =3D {=0A=
>>   			BIT(IIO_CHAN_INFO_OFFSET) |=0A=
>>   			BIT(IIO_CHAN_INFO_SCALE),=0A=
>>   		.info_mask_shared_by_all =3D BIT(IIO_CHAN_INFO_SAMP_FREQ),=0A=
>> +		.scan_index =3D RPR0521_CHAN_INDEX_PXS,=0A=
>> +		.scan_type =3D {=0A=
>> +			.sign =3D 'u',=0A=
>> +			.realbits =3D 16,=0A=
>> +			.storagebits =3D 16,=0A=
>> +			.endianness =3D IIO_LE,=0A=
>> +		},=0A=
>>   	},=0A=
>>   	{=0A=
>>   		.type =3D IIO_INTENSITY,=0A=
>> @@ -213,6 +254,13 @@ static const struct iio_chan_spec rpr0521_channels[=
] =3D {=0A=
>>   		.info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) |=0A=
>>   			BIT(IIO_CHAN_INFO_SCALE),=0A=
>>   		.info_mask_shared_by_all =3D BIT(IIO_CHAN_INFO_SAMP_FREQ),=0A=
>> +		.scan_index =3D RPR0521_CHAN_INDEX_BOTH,=0A=
>> +		.scan_type =3D {=0A=
>> +			.sign =3D 'u',=0A=
>> +			.realbits =3D 16,=0A=
>> +			.storagebits =3D 16,=0A=
>> +			.endianness =3D IIO_LE,=0A=
>> +		},=0A=
>>   	},=0A=
>>   	{=0A=
>>   		.type =3D IIO_INTENSITY,=0A=
>> @@ -222,6 +270,13 @@ static const struct iio_chan_spec rpr0521_channels[=
] =3D {=0A=
>>   		.info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) |=0A=
>>   			BIT(IIO_CHAN_INFO_SCALE),=0A=
>>   		.info_mask_shared_by_all =3D BIT(IIO_CHAN_INFO_SAMP_FREQ),=0A=
>> +		.scan_index =3D RPR0521_CHAN_INDEX_IR,=0A=
>> +		.scan_type =3D {=0A=
>> +			.sign =3D 'u',=0A=
>> +			.realbits =3D 16,=0A=
>> +			.storagebits =3D 16,=0A=
>> +			.endianness =3D IIO_LE,=0A=
>> +		},=0A=
>>   	},=0A=
>>   };=0A=
>>   =0A=
>> @@ -330,6 +385,150 @@ static int rpr0521_set_power_state(struct rpr0521_=
data *data, bool on,=0A=
>>   	return 0;=0A=
>>   }=0A=
>>   =0A=
>> +/* IRQ to trigger handler */=0A=
>> +static irqreturn_t rpr0521_drdy_irq_handler(int irq, void *private)=0A=
>> +{=0A=
>> +	struct iio_dev *indio_dev =3D private;=0A=
>> +	struct rpr0521_data *data =3D iio_priv(indio_dev);=0A=
>> +=0A=
>> +	/*=0A=
>> +	 * Sometimes static on floating (hi-z) interrupt line causes interrupt=
.=0A=
>> +	 * Notify trigger0 consumers/subscribers only if trigger has been=0A=
>> +	 * enabled. This is to prevent i2c writes to sensor which is actually=
=0A=
>> +	 * powered off.=0A=
>> +	 */=0A=
>> +	if (data->drdy_trigger_on)=0A=
>> +		iio_trigger_poll(data->drdy_trigger0);=0A=
>> +=0A=
> If this isn't our interrupt we should really be returning IRQ_NONE=0A=
> rather than IRQ_HANDLED.  That will trigger unhandled interrupt=0A=
> messages etc in the log...=0A=
=0A=
Ack. Adding shared irq support.=0A=
=0A=
>> +	return IRQ_HANDLED;=0A=
>> +}=0A=
>> +=0A=
>> +static irqreturn_t rpr0521_trigger_consumer_handler(int irq, void *p)=
=0A=
>> +{=0A=
>> +	struct iio_poll_func *pf =3D p;=0A=
>> +	struct iio_dev *indio_dev =3D pf->indio_dev;=0A=
>> +	struct rpr0521_data *data =3D iio_priv(indio_dev);=0A=
>> +	int err;=0A=
>> +=0A=
>> +	u8 buffer[16]; /* 3 16-bit channels + padding + ts */=0A=
>> +=0A=
>> +	err =3D regmap_bulk_read(data->regmap, RPR0521_REG_PXS_DATA,=0A=
>> +		&buffer,=0A=
>> +		(3 * 2) + 1);	/* 3 * 16-bit + (discarded) int clear reg. */=0A=
>> +	if (!err)=0A=
>> +		iio_push_to_buffers_with_timestamp(indio_dev,=0A=
>> +						   buffer, pf->timestamp);=0A=
>> +	else=0A=
>> +		dev_err(&data->client->dev,=0A=
>> +			"Trigger consumer can't read from sensor.\n");=0A=
>> +=0A=
>> +	iio_trigger_notify_done(indio_dev->trig);=0A=
>> +=0A=
>> +	return IRQ_HANDLED;=0A=
>> +}=0A=
>> +=0A=
>> +static int rpr0521_write_int_enable(struct rpr0521_data *data)=0A=
>> +{=0A=
>> +	int err;=0A=
>> +=0A=
>> +	/* Interrupt after each measurement */=0A=
>> +	err =3D regmap_update_bits(data->regmap, RPR0521_REG_PXS_CTRL,=0A=
>> +		RPR0521_PXS_PERSISTENCE_MASK,=0A=
>> +		RPR0521_PXS_PERSISTENCE_DRDY);=0A=
>> +	if (err) {=0A=
>> +		dev_err(&data->client->dev, "PS control reg write fail.\n");=0A=
>> +		return -EBUSY;=0A=
>> +		}=0A=
>> +=0A=
>> +	/* Ignore latch and mode because of drdy */=0A=
>> +	err =3D regmap_write(data->regmap, RPR0521_REG_INTERRUPT,=0A=
>> +		RPR0521_INTERRUPT_INT_REASSERT_DISABLE |=0A=
>> +		RPR0521_INTERRUPT_INT_TRIG_ALS_DISABLE |=0A=
>> +		RPR0521_INTERRUPT_INT_TRIG_PS_ENABLE=0A=
>> +		);=0A=
>> +	if (err) {=0A=
>> +		dev_err(&data->client->dev, "Interrupt setup write fail.\n");=0A=
>> +		return -EBUSY;=0A=
>> +		}=0A=
>> +=0A=
>> +	return 0;=0A=
>> +}=0A=
>> +=0A=
>> +static int rpr0521_write_int_disable(struct rpr0521_data *data)=0A=
>> +{=0A=
>> +	/* Don't care of clearing mode, assert and latch. */=0A=
>> +	return regmap_write(data->regmap, RPR0521_REG_INTERRUPT,=0A=
>> +				RPR0521_INTERRUPT_INT_TRIG_ALS_DISABLE |=0A=
>> +				RPR0521_INTERRUPT_INT_TRIG_PS_DISABLE=0A=
>> +				);=0A=
>> +}=0A=
>> +=0A=
>> +/*=0A=
>> + * Trigger producer enable / disable. Note that there will be trigs onl=
y when=0A=
>> + * measurement data is ready to be read.=0A=
>> + */=0A=
>> +static int rpr0521_pxs_drdy_set_state(struct iio_trigger *trigger,=0A=
>> +	bool enable_drdy)=0A=
>> +{=0A=
>> +	struct iio_dev *indio_dev =3D iio_trigger_get_drvdata(trigger);=0A=
>> +	struct rpr0521_data *data =3D iio_priv(indio_dev);=0A=
>> +	int err;=0A=
>> +=0A=
>> +	if (enable_drdy)=0A=
>> +		err =3D rpr0521_write_int_enable(data);=0A=
>> +	else=0A=
>> +		err =3D rpr0521_write_int_disable(data);=0A=
>> +	if (err)=0A=
>> +		dev_err(&data->client->dev, "rpr0521_pxs_drdy_set_state failed\n");=
=0A=
>> +	else=0A=
>> +		data->drdy_trigger_on =3D enable_drdy;=0A=
>> +=0A=
>> +	return err;=0A=
>> +}=0A=
>> +=0A=
>> +static const struct iio_trigger_ops rpr0521_trigger_ops =3D {=0A=
>> +	.set_trigger_state =3D rpr0521_pxs_drdy_set_state,=0A=
>> +	.owner =3D THIS_MODULE,=0A=
>> +	};=0A=
>> +=0A=
>> +=0A=
>> +static int rpr0521_buffer_preenable(struct iio_dev *indio_dev)=0A=
>> +{=0A=
>> +	int err;=0A=
>> +	struct rpr0521_data *data =3D iio_priv(indio_dev);=0A=
>> +=0A=
>> +	mutex_lock(&data->lock);=0A=
>> +	err =3D rpr0521_set_power_state(data, true,=0A=
>> +		(RPR0521_MODE_PXS_MASK | RPR0521_MODE_ALS_MASK));=0A=
>> +	mutex_unlock(&data->lock);=0A=
>> +	if (err)=0A=
>> +		dev_err(&data->client->dev, "_buffer_preenable fail\n");=0A=
>> +=0A=
>> +	return err;=0A=
>> +}=0A=
>> +=0A=
>> +static int rpr0521_buffer_postdisable(struct iio_dev *indio_dev)=0A=
>> +{=0A=
>> +	int err;=0A=
>> +	struct rpr0521_data *data =3D iio_priv(indio_dev);=0A=
>> +=0A=
>> +	mutex_lock(&data->lock);=0A=
>> +	err =3D rpr0521_set_power_state(data, false,=0A=
>> +		(RPR0521_MODE_PXS_MASK | RPR0521_MODE_ALS_MASK));=0A=
>> +	mutex_unlock(&data->lock);=0A=
>> +	if (err)=0A=
>> +		dev_err(&data->client->dev, "_buffer_postdisable fail\n");=0A=
>> +=0A=
>> +	return err;=0A=
>> +}=0A=
>> +=0A=
>> +static const struct iio_buffer_setup_ops rpr0521_buffer_setup_ops =3D {=
=0A=
>> +	.preenable =3D rpr0521_buffer_preenable,=0A=
>> +	.postenable =3D iio_triggered_buffer_postenable,=0A=
>> +	.predisable =3D iio_triggered_buffer_predisable,=0A=
>> +	.postdisable =3D rpr0521_buffer_postdisable,=0A=
>> +};=0A=
>> +=0A=
>>   static int rpr0521_get_gain(struct rpr0521_data *data, int chan,=0A=
>>   			    int *val, int *val2)=0A=
>>   {=0A=
>> @@ -481,6 +680,9 @@ static int rpr0521_read_raw(struct iio_dev *indio_de=
v,=0A=
>>   		if (chan->type !=3D IIO_INTENSITY && chan->type !=3D IIO_PROXIMITY)=
=0A=
>>   			return -EINVAL;=0A=
>>   =0A=
>> +		if (iio_buffer_enabled(indio_dev))=0A=
>> +			return -EBUSY;=0A=
> This is racy.  You need to actually prevent a transition to buffered read=
ing.=0A=
> That means you want to use iio_claim_direct etc to hold the lock over sta=
te=0A=
> transitions whilst the read is going on.=0A=
>=0A=
> (sorry I missed this in earlier versions!)=0A=
=0A=
Oh, you are nudging me away from the 4.4.=0A=
Np. I agree on issue and fix. However iio_device_claim_direct_mode() is=0A=
available only from 4.7 onward and I'm working and testing on top of 4.4.=
=0A=
=0A=
Since buffer preenable has mutex for powering on, I could mostly (not=0A=
fully) tackle this by extending mutex below to include this test. Single=0A=
read happening while in iio_buffer_store_enable() between preenable and=0A=
"currentmode =3D" would cause sensor to power down and not wake up on next=
=0A=
system suspend-resume.=0A=
=0A=
I can't figure out any other 4.4 fix than making similar mutex in driver=0A=
as in iio_device_claim_direct_mode(). So maybe do commit=0A=
without+separate fix commit? I can do second commit and build it but not=0A=
test it currently.=0A=
=0A=
>> +=0A=
>>   		device_mask =3D rpr0521_data_reg[chan->address].device_mask;=0A=
>>   =0A=
>>   		mutex_lock(&data->lock);=0A=
>> @@ -623,6 +825,7 @@ static int rpr0521_init(struct rpr0521_data *data)=
=0A=
>>   static int rpr0521_poweroff(struct rpr0521_data *data)=0A=
>>   {=0A=
>>   	int ret;=0A=
>> +	int tmp;=0A=
>>   =0A=
>>   	ret =3D regmap_update_bits(data->regmap, RPR0521_REG_MODE_CTRL,=0A=
>>   				 RPR0521_MODE_ALS_MASK |=0A=
>> @@ -635,6 +838,16 @@ static int rpr0521_poweroff(struct rpr0521_data *da=
ta)=0A=
>>   	data->als_dev_en =3D false;=0A=
>>   	data->pxs_dev_en =3D false;=0A=
>>   =0A=
>> +	/*=0A=
>> +	 * Int pin keeps state after power off. Set pin to high impedance=0A=
>> +	 * mode to prevent power drain.=0A=
>> +	 */=0A=
>> +	ret =3D regmap_read(data->regmap, RPR0521_REG_INTERRUPT, &tmp);=0A=
>> +	if (ret) {=0A=
>> +		dev_err(&data->client->dev, "Failed to reset int pin.\n");=0A=
>> +		return ret;=0A=
>> +	}=0A=
>> +=0A=
>>   	return 0;=0A=
>>   }=0A=
>>   =0A=
>> @@ -707,12 +920,78 @@ static int rpr0521_probe(struct i2c_client *client=
,=0A=
>>   	pm_runtime_set_autosuspend_delay(&client->dev, RPR0521_SLEEP_DELAY_MS=
);=0A=
>>   	pm_runtime_use_autosuspend(&client->dev);=0A=
>>   =0A=
>> +	/*=0A=
>> +	 * If sensor write/read  is needed in _probe after _use_autosuspend,=
=0A=
>> +	 * sensor needs to be _resumed first using rpr0521_set_power_state().=
=0A=
>> +	 */=0A=
>> +=0A=
>> +	/* IRQ to trigger setup */=0A=
>> +	if (client->irq) {=0A=
>> +		/* Trigger0 producer setup */=0A=
>> +		data->drdy_trigger0 =3D devm_iio_trigger_alloc(=0A=
>> +			indio_dev->dev.parent,=0A=
>> +			"%s-dev%d", indio_dev->name, indio_dev->id);=0A=
>> +		if (!data->drdy_trigger0) {=0A=
>> +			ret =3D -ENOMEM;=0A=
>> +			goto err_pm_disable;=0A=
>> +		}=0A=
>> +		data->drdy_trigger0->dev.parent =3D indio_dev->dev.parent;=0A=
>> +		data->drdy_trigger0->ops =3D &rpr0521_trigger_ops;=0A=
>> +		indio_dev->available_scan_masks =3D rpr0521_available_scan_masks;=0A=
>> +		iio_trigger_set_drvdata(data->drdy_trigger0, indio_dev);=0A=
>> +=0A=
>> +		/* Ties irq to trigger producer handler. */=0A=
>> +		ret =3D devm_request_threaded_irq(&client->dev, client->irq,=0A=
>> +			rpr0521_drdy_irq_handler,=0A=
>> +			NULL,=0A=
>> +			IRQF_TRIGGER_FALLING | IRQF_ONESHOT,=0A=
>> +			RPR0521_IRQ_NAME,=0A=
>> +			indio_dev);=0A=
>> +		if (ret < 0) {=0A=
>> +			dev_err(&client->dev, "request irq %d for trigger0 failed\n",=0A=
>> +				client->irq);=0A=
>> +			goto err_pm_disable;=0A=
>> +			}=0A=
>> +=0A=
>> +		ret =3D iio_trigger_register(data->drdy_trigger0);=0A=
>> +		if (ret) {=0A=
>> +			dev_err(&client->dev, "iio trigger register failed\n");=0A=
>> +			goto err_pm_disable;=0A=
>> +		}=0A=
>> +=0A=
>> +		/*=0A=
>> +		 * Now whole pipe from physical interrupt (irq defined by=0A=
>> +		 * devicetree to device) to trigger0 output is set up.=0A=
>> +		 */=0A=
>> +=0A=
>> +		/* Trigger consumer setup */=0A=
>> +		ret =3D iio_triggered_buffer_setup(indio_dev,=0A=
>> +			&iio_pollfunc_store_time,=0A=
>> +			rpr0521_trigger_consumer_handler,=0A=
>> +			&rpr0521_buffer_setup_ops);=0A=
>> +		if (ret < 0) {=0A=
>> +			dev_err(&client->dev, "iio triggered buffer setup failed\n");=0A=
>> +			/* Count on devm to free_irq */=0A=
>> +			goto err_iio_trigger_unregister;=0A=
>> +		}=0A=
>> +	}=0A=
>> +=0A=
>>   	ret =3D iio_device_register(indio_dev);=0A=
>>   	if (ret)=0A=
>> -		goto err_pm_disable;=0A=
>> +		goto err_iio_triggered_buffer_cleanup;=0A=
>> +=0A=
>> +	if (client->irq) {=0A=
>> +		/* Set trigger0 as current trigger (and inc refcount) */=0A=
>> +		indio_dev->trig =3D iio_trigger_get(data->drdy_trigger0);=0A=
>> +	}=0A=
>>   =0A=
>>   	return 0;=0A=
>>   =0A=
>> +err_iio_triggered_buffer_cleanup:=0A=
>> +	iio_triggered_buffer_cleanup(indio_dev);=0A=
>> +err_iio_trigger_unregister:=0A=
>> +	if (data->drdy_trigger0)=0A=
>> +		iio_trigger_unregister(data->drdy_trigger0);=0A=
>>   err_pm_disable:=0A=
>>   	pm_runtime_disable(&client->dev);=0A=
>>   	pm_runtime_set_suspended(&client->dev);=0A=
>> @@ -726,14 +1005,23 @@ err_poweroff:=0A=
>>   static int rpr0521_remove(struct i2c_client *client)=0A=
>>   {=0A=
>>   	struct iio_dev *indio_dev =3D i2c_get_clientdata(client);=0A=
>> +	struct rpr0521_data *data =3D iio_priv(indio_dev);=0A=
>> +=0A=
>> +	if (indio_dev->trig)=0A=
>> +		iio_trigger_put(indio_dev->trig);=0A=
> There is not guarantee this is the same trigger as that=0A=
> provided by this driver.=0A=
=0A=
It doesn't matter. In that case iio_trigger_write_current() has put the=0A=
default one and has get the new one. Then we just put the new one here.=0A=
=0A=
> So strangely the right option here=0A=
> is to get it as you have done which will set up the default=0A=
> to be sane, but to allow userspace to be responsible for the=0A=
> put by clearing current_trigger if it wants to remove the=0A=
> driver.  As a general rule I'm fairly anti default triggers=0A=
> but I do appreciate they are sometimes all that makes sense...=0A=
=0A=
If defaults are not good habit, then I think it should be fine to not=0A=
get default trigger just and rely on userspace getting it when needed.=0A=
Didn't test it yet, but I think that should be fine with IIO_HAL. Even=0A=
in that case I would _put() it in remove().=0A=
=0A=
> However, if that is the case, then you should have the various=0A=
> validation callbacks provided to ensure this trigger is used=0A=
> only for this device and the device can only use this trigger.=0A=
>=0A=
> You 'could' set indio_dev->trig_readonly which will block=0A=
> out userspace changes...  Perhaps that's what you want to=0A=
> do here.=0A=
>>   =0A=
>>   	iio_device_unregister(indio_dev);=0A=
>>   =0A=
>> +	iio_triggered_buffer_cleanup(indio_dev);=0A=
>> +=0A=
>> +	if (data->drdy_trigger0)=0A=
>> +		iio_trigger_unregister(data->drdy_trigger0);=0A=
>> +=0A=
>>   	pm_runtime_disable(&client->dev);=0A=
>>   	pm_runtime_set_suspended(&client->dev);=0A=
>>   	pm_runtime_put_noidle(&client->dev);=0A=
>>   =0A=
>> -	rpr0521_poweroff(iio_priv(indio_dev));=0A=
>> +	rpr0521_poweroff(data);=0A=
>>   =0A=
>>   	return 0;=0A=
>>   }=0A=
>>=0A=
=0A=

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v5 9/9] iio: light: rpr0521 triggered buffer
  2017-05-25 13:53     ` Koivunen, Mikko
@ 2017-05-28 15:06       ` Jonathan Cameron
  0 siblings, 0 replies; 22+ messages in thread
From: Jonathan Cameron @ 2017-05-28 15:06 UTC (permalink / raw)
  To: Koivunen, Mikko; +Cc: pmeerw, knaack.h, lars, Daniel Baluta, linux-iio

On Thu, 25 May 2017 13:53:03 +0000
"Koivunen, Mikko" <Mikko.Koivunen@fi.rohmeurope.com> wrote:

> On 21.5.2017 14:34, Jonathan Cameron wrote:
> > On 18/05/17 13:12, Mikko Koivunen wrote:  
> >> Set up and use triggered buffer if there is irq defined for device in
> >> device tree. Trigger producer triggers from rpr0521 drdy interrupt line.
> >> Trigger consumer reads rpr0521 data to scan buffer.
> >> Depends on previous commits of _scale and _offset.
> >>
> >> Signed-off-by: Mikko Koivunen <mikko.koivunen@fi.rohmeurope.com>  
> > Hi Mikko,
> >
> > A few last bits in the final patch of the series.  See inline.
> > The IRQ_NONE one might trigger some nasty issues and ultimately
> > disable the interrupt.   
> 
> Do you have any pointer for more information about that?
Sure - just follow the code.  I can't recall why I first ended
up looking at this, but the irq code in general is quite clean
and elegant so it is usually easier than trying to find any 
docs ;)

In this particular case start in kernel/irq/handle.c and dive
down to handle_irq_event_per_cpu which calls (unless it is
disabled) note_interrupt which is in kernel/irq/spurious.c

That has an if statement
if (unlikely(action_ret == IRQ_NONE)) with some logic that can
ultimately disable the IRQ, though it takes quite a few interrupts
to trigger this.
> 
> > However, it is the right option if we know
> > it's not our interrupt...  
> 
> Ok, agree. It's also mandatory for shared interrupt -> changing.
> 
> > Jonathan  
> >> ---
> >> Patch v2->v3 changes:
> >> .shift = 0 removed
> >> reordered functions to remove forward declarations
> >> whitespace changes
> >> refactored some update_bits->write, no "err = err || *"-pattern
> >> refactored trigger_consumer_handler
> >> reordered _probe() and _remove()
> >> added int clear on poweroff()
> >> checkpatch.pl OK
> >> Tested on LeMaker HiKey with AOSP7.1 kernel 4.4.
> >> Builds ok with torvalds/linux feb 27.
> >>
> >> Patch v3->v4 changes:
> >> Moved sensor on/off commands to buffer preenable and postdisable
> >>   - Since drdy happens only on measurement data ready and register writes
> >>     are cached, the trigger producer doesn't care of suspend/resume state.
> >> available_scan_masks added
> >> indent fix
> >> checkpatch.pl OK
> >> Lightly re-tested on LeMaker HiKey with AOSP7.1 kernel 4.4.
> >> Builds ok with torvalds/linux feb 27.
> >>
> >>   drivers/iio/light/rpr0521.c |  294 ++++++++++++++++++++++++++++++++++++++++++-
> >>   1 file changed, 291 insertions(+), 3 deletions(-)
> >>
> >> diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
> >> index 9d0c2e8..1ef6060 100644
> >> --- a/drivers/iio/light/rpr0521.c
> >> +++ b/drivers/iio/light/rpr0521.c
> >> @@ -9,7 +9,7 @@
> >>    *
> >>    * IIO driver for RPR-0521RS (7-bit I2C slave address 0x38).
> >>    *
> >> - * TODO: illuminance channel, buffer
> >> + * TODO: illuminance channel
> >>    */
> >>   
> >>   #include <linux/module.h>
> >> @@ -20,6 +20,10 @@
> >>   #include <linux/acpi.h>
> >>   
> >>   #include <linux/iio/iio.h>
> >> +#include <linux/iio/buffer.h>
> >> +#include <linux/iio/trigger.h>
> >> +#include <linux/iio/trigger_consumer.h>
> >> +#include <linux/iio/triggered_buffer.h>
> >>   #include <linux/iio/sysfs.h>
> >>   #include <linux/pm_runtime.h>
> >>   
> >> @@ -30,6 +34,7 @@
> >>   #define RPR0521_REG_PXS_DATA		0x44 /* 16-bit, little endian */
> >>   #define RPR0521_REG_ALS_DATA0		0x46 /* 16-bit, little endian */
> >>   #define RPR0521_REG_ALS_DATA1		0x48 /* 16-bit, little endian */
> >> +#define RPR0521_REG_INTERRUPT		0x4A
> >>   #define RPR0521_REG_PS_OFFSET_LSB	0x53
> >>   #define RPR0521_REG_ID			0x92
> >>   
> >> @@ -42,16 +47,29 @@
> >>   #define RPR0521_ALS_DATA1_GAIN_SHIFT	2
> >>   #define RPR0521_PXS_GAIN_MASK		GENMASK(5, 4)
> >>   #define RPR0521_PXS_GAIN_SHIFT		4
> >> +#define RPR0521_PXS_PERSISTENCE_MASK	GENMASK(3, 0)
> >> +#define RPR0521_INTERRUPT_INT_TRIG_PS_MASK	BIT(0)
> >> +#define RPR0521_INTERRUPT_INT_TRIG_ALS_MASK	BIT(1)
> >> +#define RPR0521_INTERRUPT_INT_REASSERT_MASK	BIT(3)
> >>   
> >>   #define RPR0521_MODE_ALS_ENABLE		BIT(7)
> >>   #define RPR0521_MODE_ALS_DISABLE	0x00
> >>   #define RPR0521_MODE_PXS_ENABLE		BIT(6)
> >>   #define RPR0521_MODE_PXS_DISABLE	0x00
> >> +#define RPR0521_PXS_PERSISTENCE_DRDY	0x00
> >> +
> >> +#define RPR0521_INTERRUPT_INT_TRIG_PS_ENABLE	BIT(0)
> >> +#define RPR0521_INTERRUPT_INT_TRIG_PS_DISABLE	0x00
> >> +#define RPR0521_INTERRUPT_INT_TRIG_ALS_ENABLE	BIT(1)
> >> +#define RPR0521_INTERRUPT_INT_TRIG_ALS_DISABLE	0x00
> >> +#define RPR0521_INTERRUPT_INT_REASSERT_ENABLE	BIT(3)
> >> +#define RPR0521_INTERRUPT_INT_REASSERT_DISABLE	0x00
> >>   
> >>   #define RPR0521_MANUFACT_ID		0xE0
> >>   #define RPR0521_DEFAULT_MEAS_TIME	0x06 /* ALS - 100ms, PXS - 100ms */
> >>   
> >>   #define RPR0521_DRV_NAME		"RPR0521"
> >> +#define RPR0521_IRQ_NAME		"rpr0521_event"
> >>   #define RPR0521_REGMAP_NAME		"rpr0521_regmap"
> >>   
> >>   #define RPR0521_SLEEP_DELAY_MS	2000
> >> @@ -167,6 +185,9 @@ struct rpr0521_data {
> >>   	bool als_dev_en;
> >>   	bool pxs_dev_en;
> >>   
> >> +	struct iio_trigger *drdy_trigger0;
> >> +	bool drdy_trigger_on;
> >> +
> >>   	/* optimize runtime pm ops - enable/disable device only if needed */
> >>   	bool als_ps_need_en;
> >>   	bool pxs_ps_need_en;
> >> @@ -196,6 +217,19 @@ static const struct attribute_group rpr0521_attribute_group = {
> >>   	.attrs = rpr0521_attributes,
> >>   };
> >>   
> >> +/* Order of the channel data in buffer */
> >> +enum rpr0521_scan_index_order {
> >> +	RPR0521_CHAN_INDEX_PXS,
> >> +	RPR0521_CHAN_INDEX_BOTH,
> >> +	RPR0521_CHAN_INDEX_IR,
> >> +};
> >> +
> >> +static const unsigned long rpr0521_available_scan_masks[] = {
> >> +	BIT(RPR0521_CHAN_INDEX_PXS) | BIT(RPR0521_CHAN_INDEX_BOTH) |
> >> +	BIT(RPR0521_CHAN_INDEX_IR),
> >> +	0
> >> +};
> >> +
> >>   static const struct iio_chan_spec rpr0521_channels[] = {
> >>   	{
> >>   		.type = IIO_PROXIMITY,
> >> @@ -204,6 +238,13 @@ static const struct iio_chan_spec rpr0521_channels[] = {
> >>   			BIT(IIO_CHAN_INFO_OFFSET) |
> >>   			BIT(IIO_CHAN_INFO_SCALE),
> >>   		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
> >> +		.scan_index = RPR0521_CHAN_INDEX_PXS,
> >> +		.scan_type = {
> >> +			.sign = 'u',
> >> +			.realbits = 16,
> >> +			.storagebits = 16,
> >> +			.endianness = IIO_LE,
> >> +		},
> >>   	},
> >>   	{
> >>   		.type = IIO_INTENSITY,
> >> @@ -213,6 +254,13 @@ static const struct iio_chan_spec rpr0521_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 = RPR0521_CHAN_INDEX_BOTH,
> >> +		.scan_type = {
> >> +			.sign = 'u',
> >> +			.realbits = 16,
> >> +			.storagebits = 16,
> >> +			.endianness = IIO_LE,
> >> +		},
> >>   	},
> >>   	{
> >>   		.type = IIO_INTENSITY,
> >> @@ -222,6 +270,13 @@ static const struct iio_chan_spec rpr0521_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 = RPR0521_CHAN_INDEX_IR,
> >> +		.scan_type = {
> >> +			.sign = 'u',
> >> +			.realbits = 16,
> >> +			.storagebits = 16,
> >> +			.endianness = IIO_LE,
> >> +		},
> >>   	},
> >>   };
> >>   
> >> @@ -330,6 +385,150 @@ static int rpr0521_set_power_state(struct rpr0521_data *data, bool on,
> >>   	return 0;
> >>   }
> >>   
> >> +/* IRQ to trigger handler */
> >> +static irqreturn_t rpr0521_drdy_irq_handler(int irq, void *private)
> >> +{
> >> +	struct iio_dev *indio_dev = private;
> >> +	struct rpr0521_data *data = iio_priv(indio_dev);
> >> +
> >> +	/*
> >> +	 * Sometimes static on floating (hi-z) interrupt line causes interrupt.
> >> +	 * Notify trigger0 consumers/subscribers only if trigger has been
> >> +	 * enabled. This is to prevent i2c writes to sensor which is actually
> >> +	 * powered off.
> >> +	 */
> >> +	if (data->drdy_trigger_on)
> >> +		iio_trigger_poll(data->drdy_trigger0);
> >> +  
> > If this isn't our interrupt we should really be returning IRQ_NONE
> > rather than IRQ_HANDLED.  That will trigger unhandled interrupt
> > messages etc in the log...  
> 
> Ack. Adding shared irq support.
> 
> >> +	return IRQ_HANDLED;
> >> +}
> >> +
> >> +static irqreturn_t rpr0521_trigger_consumer_handler(int irq, void *p)
> >> +{
> >> +	struct iio_poll_func *pf = p;
> >> +	struct iio_dev *indio_dev = pf->indio_dev;
> >> +	struct rpr0521_data *data = iio_priv(indio_dev);
> >> +	int err;
> >> +
> >> +	u8 buffer[16]; /* 3 16-bit channels + padding + ts */
> >> +
> >> +	err = regmap_bulk_read(data->regmap, RPR0521_REG_PXS_DATA,
> >> +		&buffer,
> >> +		(3 * 2) + 1);	/* 3 * 16-bit + (discarded) int clear reg. */
> >> +	if (!err)
> >> +		iio_push_to_buffers_with_timestamp(indio_dev,
> >> +						   buffer, pf->timestamp);
> >> +	else
> >> +		dev_err(&data->client->dev,
> >> +			"Trigger consumer can't read from sensor.\n");
> >> +
> >> +	iio_trigger_notify_done(indio_dev->trig);
> >> +
> >> +	return IRQ_HANDLED;
> >> +}
> >> +
> >> +static int rpr0521_write_int_enable(struct rpr0521_data *data)
> >> +{
> >> +	int err;
> >> +
> >> +	/* Interrupt after each measurement */
> >> +	err = regmap_update_bits(data->regmap, RPR0521_REG_PXS_CTRL,
> >> +		RPR0521_PXS_PERSISTENCE_MASK,
> >> +		RPR0521_PXS_PERSISTENCE_DRDY);
> >> +	if (err) {
> >> +		dev_err(&data->client->dev, "PS control reg write fail.\n");
> >> +		return -EBUSY;
> >> +		}
> >> +
> >> +	/* Ignore latch and mode because of drdy */
> >> +	err = regmap_write(data->regmap, RPR0521_REG_INTERRUPT,
> >> +		RPR0521_INTERRUPT_INT_REASSERT_DISABLE |
> >> +		RPR0521_INTERRUPT_INT_TRIG_ALS_DISABLE |
> >> +		RPR0521_INTERRUPT_INT_TRIG_PS_ENABLE
> >> +		);
> >> +	if (err) {
> >> +		dev_err(&data->client->dev, "Interrupt setup write fail.\n");
> >> +		return -EBUSY;
> >> +		}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int rpr0521_write_int_disable(struct rpr0521_data *data)
> >> +{
> >> +	/* Don't care of clearing mode, assert and latch. */
> >> +	return regmap_write(data->regmap, RPR0521_REG_INTERRUPT,
> >> +				RPR0521_INTERRUPT_INT_TRIG_ALS_DISABLE |
> >> +				RPR0521_INTERRUPT_INT_TRIG_PS_DISABLE
> >> +				);
> >> +}
> >> +
> >> +/*
> >> + * Trigger producer enable / disable. Note that there will be trigs only when
> >> + * measurement data is ready to be read.
> >> + */
> >> +static int rpr0521_pxs_drdy_set_state(struct iio_trigger *trigger,
> >> +	bool enable_drdy)
> >> +{
> >> +	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trigger);
> >> +	struct rpr0521_data *data = iio_priv(indio_dev);
> >> +	int err;
> >> +
> >> +	if (enable_drdy)
> >> +		err = rpr0521_write_int_enable(data);
> >> +	else
> >> +		err = rpr0521_write_int_disable(data);
> >> +	if (err)
> >> +		dev_err(&data->client->dev, "rpr0521_pxs_drdy_set_state failed\n");
> >> +	else
> >> +		data->drdy_trigger_on = enable_drdy;
> >> +
> >> +	return err;
> >> +}
> >> +
> >> +static const struct iio_trigger_ops rpr0521_trigger_ops = {
> >> +	.set_trigger_state = rpr0521_pxs_drdy_set_state,
> >> +	.owner = THIS_MODULE,
> >> +	};
> >> +
> >> +
> >> +static int rpr0521_buffer_preenable(struct iio_dev *indio_dev)
> >> +{
> >> +	int err;
> >> +	struct rpr0521_data *data = iio_priv(indio_dev);
> >> +
> >> +	mutex_lock(&data->lock);
> >> +	err = rpr0521_set_power_state(data, true,
> >> +		(RPR0521_MODE_PXS_MASK | RPR0521_MODE_ALS_MASK));
> >> +	mutex_unlock(&data->lock);
> >> +	if (err)
> >> +		dev_err(&data->client->dev, "_buffer_preenable fail\n");
> >> +
> >> +	return err;
> >> +}
> >> +
> >> +static int rpr0521_buffer_postdisable(struct iio_dev *indio_dev)
> >> +{
> >> +	int err;
> >> +	struct rpr0521_data *data = iio_priv(indio_dev);
> >> +
> >> +	mutex_lock(&data->lock);
> >> +	err = rpr0521_set_power_state(data, false,
> >> +		(RPR0521_MODE_PXS_MASK | RPR0521_MODE_ALS_MASK));
> >> +	mutex_unlock(&data->lock);
> >> +	if (err)
> >> +		dev_err(&data->client->dev, "_buffer_postdisable fail\n");
> >> +
> >> +	return err;
> >> +}
> >> +
> >> +static const struct iio_buffer_setup_ops rpr0521_buffer_setup_ops = {
> >> +	.preenable = rpr0521_buffer_preenable,
> >> +	.postenable = iio_triggered_buffer_postenable,
> >> +	.predisable = iio_triggered_buffer_predisable,
> >> +	.postdisable = rpr0521_buffer_postdisable,
> >> +};
> >> +
> >>   static int rpr0521_get_gain(struct rpr0521_data *data, int chan,
> >>   			    int *val, int *val2)
> >>   {
> >> @@ -481,6 +680,9 @@ static int rpr0521_read_raw(struct iio_dev *indio_dev,
> >>   		if (chan->type != IIO_INTENSITY && chan->type != IIO_PROXIMITY)
> >>   			return -EINVAL;
> >>   
> >> +		if (iio_buffer_enabled(indio_dev))
> >> +			return -EBUSY;  
> > This is racy.  You need to actually prevent a transition to buffered reading.
> > That means you want to use iio_claim_direct etc to hold the lock over state
> > transitions whilst the read is going on.
> >
> > (sorry I missed this in earlier versions!)  
> 
> Oh, you are nudging me away from the 4.4.
> Np. I agree on issue and fix. However iio_device_claim_direct_mode() is
> available only from 4.7 onward and I'm working and testing on top of 4.4.
> 
> Since buffer preenable has mutex for powering on, I could mostly (not
> fully) tackle this by extending mutex below to include this test. Single
> read happening while in iio_buffer_store_enable() between preenable and
> "currentmode =" would cause sensor to power down and not wake up on next
> system suspend-resume.
> 
> I can't figure out any other 4.4 fix than making similar mutex in driver
> as in iio_device_claim_direct_mode(). So maybe do commit
> without+separate fix commit? I can do second commit and build it but not
> test it currently.
You could hand role the code that iio_device_claim_direct_mode is doing.
I'm fairly sure the infrastructure was always there and this wrapper
just made sure that it was correctly used.  Then a follow up patch
to just replace that with using iio_device_claim_direct_mode.

I'd be happy with that if it makes your life easier.
> 
> >> +
> >>   		device_mask = rpr0521_data_reg[chan->address].device_mask;
> >>   
> >>   		mutex_lock(&data->lock);
> >> @@ -623,6 +825,7 @@ static int rpr0521_init(struct rpr0521_data *data)
> >>   static int rpr0521_poweroff(struct rpr0521_data *data)
> >>   {
> >>   	int ret;
> >> +	int tmp;
> >>   
> >>   	ret = regmap_update_bits(data->regmap, RPR0521_REG_MODE_CTRL,
> >>   				 RPR0521_MODE_ALS_MASK |
> >> @@ -635,6 +838,16 @@ static int rpr0521_poweroff(struct rpr0521_data *data)
> >>   	data->als_dev_en = false;
> >>   	data->pxs_dev_en = false;
> >>   
> >> +	/*
> >> +	 * Int pin keeps state after power off. Set pin to high impedance
> >> +	 * mode to prevent power drain.
> >> +	 */
> >> +	ret = regmap_read(data->regmap, RPR0521_REG_INTERRUPT, &tmp);
> >> +	if (ret) {
> >> +		dev_err(&data->client->dev, "Failed to reset int pin.\n");
> >> +		return ret;
> >> +	}
> >> +
> >>   	return 0;
> >>   }
> >>   
> >> @@ -707,12 +920,78 @@ static int rpr0521_probe(struct i2c_client *client,
> >>   	pm_runtime_set_autosuspend_delay(&client->dev, RPR0521_SLEEP_DELAY_MS);
> >>   	pm_runtime_use_autosuspend(&client->dev);
> >>   
> >> +	/*
> >> +	 * If sensor write/read  is needed in _probe after _use_autosuspend,
> >> +	 * sensor needs to be _resumed first using rpr0521_set_power_state().
> >> +	 */
> >> +
> >> +	/* IRQ to trigger setup */
> >> +	if (client->irq) {
> >> +		/* Trigger0 producer setup */
> >> +		data->drdy_trigger0 = devm_iio_trigger_alloc(
> >> +			indio_dev->dev.parent,
> >> +			"%s-dev%d", indio_dev->name, indio_dev->id);
> >> +		if (!data->drdy_trigger0) {
> >> +			ret = -ENOMEM;
> >> +			goto err_pm_disable;
> >> +		}
> >> +		data->drdy_trigger0->dev.parent = indio_dev->dev.parent;
> >> +		data->drdy_trigger0->ops = &rpr0521_trigger_ops;
> >> +		indio_dev->available_scan_masks = rpr0521_available_scan_masks;
> >> +		iio_trigger_set_drvdata(data->drdy_trigger0, indio_dev);
> >> +
> >> +		/* Ties irq to trigger producer handler. */
> >> +		ret = devm_request_threaded_irq(&client->dev, client->irq,
> >> +			rpr0521_drdy_irq_handler,
> >> +			NULL,
> >> +			IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
> >> +			RPR0521_IRQ_NAME,
> >> +			indio_dev);
> >> +		if (ret < 0) {
> >> +			dev_err(&client->dev, "request irq %d for trigger0 failed\n",
> >> +				client->irq);
> >> +			goto err_pm_disable;
> >> +			}
> >> +
> >> +		ret = iio_trigger_register(data->drdy_trigger0);
> >> +		if (ret) {
> >> +			dev_err(&client->dev, "iio trigger register failed\n");
> >> +			goto err_pm_disable;
> >> +		}
> >> +
> >> +		/*
> >> +		 * Now whole pipe from physical interrupt (irq defined by
> >> +		 * devicetree to device) to trigger0 output is set up.
> >> +		 */
> >> +
> >> +		/* Trigger consumer setup */
> >> +		ret = iio_triggered_buffer_setup(indio_dev,
> >> +			&iio_pollfunc_store_time,
> >> +			rpr0521_trigger_consumer_handler,
> >> +			&rpr0521_buffer_setup_ops);
> >> +		if (ret < 0) {
> >> +			dev_err(&client->dev, "iio triggered buffer setup failed\n");
> >> +			/* Count on devm to free_irq */
> >> +			goto err_iio_trigger_unregister;
> >> +		}
> >> +	}
> >> +
> >>   	ret = iio_device_register(indio_dev);
> >>   	if (ret)
> >> -		goto err_pm_disable;
> >> +		goto err_iio_triggered_buffer_cleanup;
> >> +
> >> +	if (client->irq) {
> >> +		/* Set trigger0 as current trigger (and inc refcount) */
> >> +		indio_dev->trig = iio_trigger_get(data->drdy_trigger0);
> >> +	}
> >>   
> >>   	return 0;
> >>   
> >> +err_iio_triggered_buffer_cleanup:
> >> +	iio_triggered_buffer_cleanup(indio_dev);
> >> +err_iio_trigger_unregister:
> >> +	if (data->drdy_trigger0)
> >> +		iio_trigger_unregister(data->drdy_trigger0);
> >>   err_pm_disable:
> >>   	pm_runtime_disable(&client->dev);
> >>   	pm_runtime_set_suspended(&client->dev);
> >> @@ -726,14 +1005,23 @@ err_poweroff:
> >>   static int rpr0521_remove(struct i2c_client *client)
> >>   {
> >>   	struct iio_dev *indio_dev = i2c_get_clientdata(client);
> >> +	struct rpr0521_data *data = iio_priv(indio_dev);
> >> +
> >> +	if (indio_dev->trig)
> >> +		iio_trigger_put(indio_dev->trig);  
> > There is not guarantee this is the same trigger as that
> > provided by this driver.  
> 
> It doesn't matter. In that case iio_trigger_write_current() has put the
> default one and has get the new one. Then we just put the new one here.
But.. It might not be any trigger at all - so this would cause a null
pointer dereference.
> 
> > So strangely the right option here
> > is to get it as you have done which will set up the default
> > to be sane, but to allow userspace to be responsible for the
> > put by clearing current_trigger if it wants to remove the
> > driver.  As a general rule I'm fairly anti default triggers
> > but I do appreciate they are sometimes all that makes sense...  
> 
> If defaults are not good habit, then I think it should be fine to not
> get default trigger just and rely on userspace getting it when needed.
> Didn't test it yet, but I think that should be fine with IIO_HAL. Even
> in that case I would _put() it in remove().
Userspace should be actively removing the trigger before trying
to remove the driver.   You should only be able to remove anyway
by forcing the remove as we will have a far from zero reference counter
on the module.

It's non standard and I don't want to see the possible bugs breaking
the standard flow could create.

I'd certainly prefer in general that the driver didn't attempt to set
up the default trigger and didn't attempt to cleanup any triggers
in remove as they should have already been disconnected cleanly before
the remove happens.

Thanks,

Jonathan
> 
> > However, if that is the case, then you should have the various
> > validation callbacks provided to ensure this trigger is used
> > only for this device and the device can only use this trigger.
> >
> > You 'could' set indio_dev->trig_readonly which will block
> > out userspace changes...  Perhaps that's what you want to
> > do here.  
> >>   
> >>   	iio_device_unregister(indio_dev);
> >>   
> >> +	iio_triggered_buffer_cleanup(indio_dev);
> >> +
> >> +	if (data->drdy_trigger0)
> >> +		iio_trigger_unregister(data->drdy_trigger0);
> >> +
> >>   	pm_runtime_disable(&client->dev);
> >>   	pm_runtime_set_suspended(&client->dev);
> >>   	pm_runtime_put_noidle(&client->dev);
> >>   
> >> -	rpr0521_poweroff(iio_priv(indio_dev));
> >> +	rpr0521_poweroff(data);
> >>   
> >>   	return 0;
> >>   }
> >>  
> 


^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2017-05-28 15:07 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-05-18 12:12 [PATCH v5 0/9] iio: light: rpr0521 Mikko Koivunen
2017-05-18 12:12 ` [PATCH v5 1/9] iio: light: rpr0521 disable sensor -bugfix Mikko Koivunen
2017-05-21 11:06   ` Jonathan Cameron
2017-05-18 12:12 ` [PATCH v5 2/9] iio: light: rpr0521 poweroff for probe fails Mikko Koivunen
2017-05-21 11:08   ` Jonathan Cameron
2017-05-18 12:12 ` [PATCH v5 3/9] iio: light: rpr0521 on-off sequence change for CONFIG_PM Mikko Koivunen
2017-05-21 11:10   ` Jonathan Cameron
2017-05-18 12:12 ` [PATCH v5 4/9] iio: light: rpr0521 magic number to sizeof() on value read Mikko Koivunen
2017-05-21 11:11   ` Jonathan Cameron
2017-05-18 12:12 ` [PATCH v5 5/9] iio: light: rpr0521 whitespace fixes Mikko Koivunen
2017-05-21 11:12   ` Jonathan Cameron
2017-05-18 12:12 ` [PATCH v5 6/9] iio: light: rpr0521 sample_frequency read/write Mikko Koivunen
2017-05-21 11:14   ` Jonathan Cameron
2017-05-18 12:12 ` [PATCH v5 7/9] iio: light: rpr0521 proximity offset read/write Mikko Koivunen
2017-05-21 11:15   ` Jonathan Cameron
2017-05-18 12:12 ` [PATCH v5 8/9] iio: light: rpr0521 channel numbers reordered Mikko Koivunen
2017-05-21 11:16   ` Jonathan Cameron
2017-05-18 12:12 ` [PATCH v5 9/9] iio: light: rpr0521 triggered buffer Mikko Koivunen
2017-05-21 11:34   ` Jonathan Cameron
2017-05-25 13:53     ` Koivunen, Mikko
2017-05-28 15:06       ` Jonathan Cameron
2017-05-18 12:20 ` [PATCH v5 0/9] iio: light: rpr0521 Daniel Baluta

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.