diff mbox series

[v2,1/3] iio: light: ltr390: Add configurable gain and resolution

Message ID 20240728151957.310237-1-abhashkumarjha123@gmail.com (mailing list archive)
State Changes Requested
Headers show
Series [v2,1/3] iio: light: ltr390: Add configurable gain and resolution | expand

Commit Message

Abhash Jha July 28, 2024, 3:19 p.m. UTC
Add support for configuring and reading the gain and resolution
    (integration time). Also provide the available values for gain and
    resoltion respectively via `read_avail` callback.

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

Comments

Jonathan Cameron July 28, 2024, 5:03 p.m. UTC | #1
On Sun, 28 Jul 2024 20:49:54 +0530
Abhash Jha <abhashkumarjha123@gmail.com> wrote:

>     Add support for configuring and reading the gain and resolution
>     (integration time). Also provide the available values for gain and
>     resoltion respectively via `read_avail` callback.
Hi Abhash,

Don't indent patch description like this.

Also from a process point of view, for IIO (and I think most of the kernel)
don't reply to a previous version thread with a new version.
The upshot is that it ends up far from the most recent emails in everyone's
inboxes as pretty much everyone uses threading.

Also, if sending multiple patches, please add a cover letter.
--cover-letter in git.

That provides a general place for comments like this one and also
gives the series a nice pretty title in patch work and similar tooling ;)
https://patchwork.kernel.org/project/linux-iio/list/?
See the series title column - that's from cover letters.

Anyhow, on to the code.  Various minor comments inline.
In particularly for v3, please look for accidental or unnecessary changes
of code formatting etc.

They add considerable noise to a fairly simple real change.

Jonathan


> 
> Signed-off-by: Abhash Jha <abhashkumarjha123@gmail.com>
> ---
>  drivers/iio/light/ltr390.c | 144 +++++++++++++++++++++++++++++++++----
>  1 file changed, 130 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/iio/light/ltr390.c b/drivers/iio/light/ltr390.c
> index fff1e8990..9f00661c3 100644
> --- a/drivers/iio/light/ltr390.c
> +++ b/drivers/iio/light/ltr390.c
> @@ -25,19 +25,26 @@
>  #include <linux/regmap.h>
>  
>  #include <linux/iio/iio.h>
> -
> +#include <linux/iio/sysfs.h>
Keep a blank line after the iio includes.
However, why this include?  You aren't defining custom attributes in this
patch.


>  #include <asm/unaligned.h>
>  
> -#define LTR390_MAIN_CTRL      0x00
> -#define LTR390_PART_ID	      0x06
> -#define LTR390_UVS_DATA	      0x10
Hmm. Annoying to have to realign this.  However, it probably isn't
worth a precursor patch for this one.
If it were many more than 3 lines it would be.

> +#define LTR390_MAIN_CTRL		0x00
> +#define LTR390_ALS_UVS_MEAS_RATE	0x04
> +#define LTR390_ALS_UVS_GAIN		0x05
> +#define LTR390_PART_ID			0x06
> +#define LTR390_ALS_DATA			0x0D
> +#define LTR390_UVS_DATA			0x10
> +#define LTR390_INT_CFG			0x19

> +
> +#define LTR390_PART_NUMBER_ID		0xb
> +#define LTR390_ALS_UVS_GAIN_MASK	0x07
> +#define LTR390_ALS_UVS_INT_TIME_MASK	0x70
> +#define LTR390_ALS_UVS_INT_TIME(x)	FIELD_PREP(LTR390_ALS_UVS_INT_TIME_MASK, x)
Brackets around x
define LTR390_ALS_UVS_INT_TIME(x)	FIELD_PREP(LTR390_ALS_UVS_INT_TIME_MASK, (x))

>  
>  #define LTR390_SW_RESET	      BIT(4)
>  #define LTR390_UVS_MODE	      BIT(3)
>  #define LTR390_SENSOR_ENABLE  BIT(1)
>  
> -#define LTR390_PART_NUMBER_ID 0xb

..

> @@ -91,32 +98,135 @@ static int ltr390_read_raw(struct iio_dev *iio_device,
>  			   struct iio_chan_spec const *chan, int *val,
>  			   int *val2, long mask)
>  {
> -	int ret;
>  	struct ltr390_data *data = iio_priv(iio_device);
> +	int ret;
>  
> +	guard(mutex)(&data->lock);
>  	switch (mask) {
>  	case IIO_CHAN_INFO_RAW:
>  		ret = ltr390_register_read(data, LTR390_UVS_DATA);
>  		if (ret < 0)
>  			return ret;
> +
Not related to the patch content, so shouldn't be here.
>  		*val = ret;
>  		return IIO_VAL_INT;
> +
Similarly.  It's a reasonable change, but not in this patch as it
adds noise.  Feel free to send another patch in the series that improves
the white space though if you like.

>  	case IIO_CHAN_INFO_SCALE:
>  		*val = LTR390_WINDOW_FACTOR;
>  		*val2 = LTR390_COUNTS_PER_UVI;
>  		return IIO_VAL_FRACTIONAL;
> +
> +	case IIO_CHAN_INFO_INT_TIME:
> +		*val = data->int_time_us;
> +		return IIO_VAL_INT;
> +
>  	default:
>  		return -EINVAL;
>  	}
>  }

...

> +/* integration time in us */
> +static const int ltr390_int_time_map_us[] = {400000, 200000, 100000, 50000, 25000, 12500};

space after { and before } preferred.

> +static const int ltr390_gain_map[] = {1, 3, 6, 9, 18};
>  
>  static const struct iio_chan_spec ltr390_channel = {
>  	.type = IIO_UVINDEX,
> -	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
> +	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
> +	.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE)
> +};
> +
> +static int ltr390_set_gain(struct ltr390_data *data, int val)
> +{
> +	int ret, idx;
> +
> +	for (idx = 0; idx < ARRAY_SIZE(ltr390_gain_map); idx++) {
> +		if (ltr390_gain_map[idx] != val)
> +			continue;
> +
> +		guard(mutex)(&data->lock);
> +		ret = regmap_update_bits(data->regmap,
> +					LTR390_ALS_UVS_GAIN,
> +					LTR390_ALS_UVS_GAIN_MASK, idx);
> +		if (ret)
> +			return ret;
> +
> +		data->gain = ltr390_gain_map[idx];
> +		break;
As below. 
		return 0;
> +	}
> +
> +	return 0;
return -EINVAL; to indicate no match.


> +}
> +
> +static int ltr390_set_int_time(struct ltr390_data *data, int val)
> +{
> +	int ret, idx;
> +
> +	for (idx = 0; idx < ARRAY_SIZE(ltr390_int_time_map_us); idx++) {
> +		if (ltr390_int_time_map_us[idx] != val)
> +			continue;
> +
> +		guard(mutex)(&data->lock);
> +		ret = regmap_update_bits(data->regmap,
> +					LTR390_ALS_UVS_MEAS_RATE,
> +					LTR390_ALS_UVS_INT_TIME_MASK,
> +					LTR390_ALS_UVS_INT_TIME(idx));
> +		if (ret)
> +			return ret;
> +
> +		data->int_time_us = ltr390_int_time_map_us[idx];
> +		break;
return 0;

No point in carrying on if we are done.

> +	}
> +
> +	return 0;
If you get here with suggested return 0 above, it will be an error as no
match occured.  In that case, return -EINVAL;

> +}

...

>  static int ltr390_probe(struct i2c_client *client)
> @@ -139,6 +249,11 @@ static int ltr390_probe(struct i2c_client *client)
>  				     "regmap initialization failed\n");
>  
>  	data->client = client;
> +	/* default value of integration time from pg: 15 of the datasheet */
> +	data->int_time_us = 100000;
> +	/* default value of gain from pg: 16 of the datasheet */
> +	data->gain = 3;
> +
>  	mutex_init(&data->lock);
>  
>  	indio_dev->info = &ltr390_info;
> @@ -162,7 +277,7 @@ static int ltr390_probe(struct i2c_client *client)
>  	usleep_range(1000, 2000);
>  
>  	ret = regmap_set_bits(data->regmap, LTR390_MAIN_CTRL,
> -			      LTR390_SENSOR_ENABLE | LTR390_UVS_MODE);
> +				LTR390_SENSOR_ENABLE | LTR390_UVS_MODE);

Avoid accidental white space changes. If you want to make them to cleanup
some inconsistencies or similar, that is fine, but they belong in a patch
that doesn't do anything else.  Here it is adding noise and slowing down
review.

>  	if (ret)
>  		return dev_err_probe(dev, ret, "failed to enable the sensor\n");
>  
> @@ -189,6 +304,7 @@ static struct i2c_driver ltr390_driver = {
>  	.probe = ltr390_probe,
>  	.id_table = ltr390_id,
>  };
> +
Don't add this line.  We often use whitespace to indicate connections and
it is common to do it in cases like this one where module_i2c_driver()
is tightly coupled with the i2c_driver structure.

>  module_i2c_driver(ltr390_driver);
>  
>  MODULE_AUTHOR("Anshul Dalal <anshulusr@gmail.com>");
diff mbox series

Patch

diff --git a/drivers/iio/light/ltr390.c b/drivers/iio/light/ltr390.c
index fff1e8990..9f00661c3 100644
--- a/drivers/iio/light/ltr390.c
+++ b/drivers/iio/light/ltr390.c
@@ -25,19 +25,26 @@ 
 #include <linux/regmap.h>
 
 #include <linux/iio/iio.h>
-
+#include <linux/iio/sysfs.h>
 #include <asm/unaligned.h>
 
-#define LTR390_MAIN_CTRL      0x00
-#define LTR390_PART_ID	      0x06
-#define LTR390_UVS_DATA	      0x10
+#define LTR390_MAIN_CTRL		0x00
+#define LTR390_ALS_UVS_MEAS_RATE	0x04
+#define LTR390_ALS_UVS_GAIN		0x05
+#define LTR390_PART_ID			0x06
+#define LTR390_ALS_DATA			0x0D
+#define LTR390_UVS_DATA			0x10
+#define LTR390_INT_CFG			0x19
+
+#define LTR390_PART_NUMBER_ID		0xb
+#define LTR390_ALS_UVS_GAIN_MASK	0x07
+#define LTR390_ALS_UVS_INT_TIME_MASK	0x70
+#define LTR390_ALS_UVS_INT_TIME(x)	FIELD_PREP(LTR390_ALS_UVS_INT_TIME_MASK, x)
 
 #define LTR390_SW_RESET	      BIT(4)
 #define LTR390_UVS_MODE	      BIT(3)
 #define LTR390_SENSOR_ENABLE  BIT(1)
 
-#define LTR390_PART_NUMBER_ID 0xb
-
 /*
  * At 20-bit resolution (integration time: 400ms) and 18x gain, 2300 counts of
  * the sensor are equal to 1 UV Index [Datasheet Page#8].
@@ -60,6 +67,8 @@  struct ltr390_data {
 	struct i2c_client *client;
 	/* Protects device from simulataneous reads */
 	struct mutex lock;
+	int gain;
+	int int_time_us;
 };
 
 static const struct regmap_config ltr390_regmap_config = {
@@ -75,8 +84,6 @@  static int ltr390_register_read(struct ltr390_data *data, u8 register_address)
 	int ret;
 	u8 recieve_buffer[3];
 
-	guard(mutex)(&data->lock);
-
 	ret = regmap_bulk_read(data->regmap, register_address, recieve_buffer,
 			       sizeof(recieve_buffer));
 	if (ret) {
@@ -91,32 +98,135 @@  static int ltr390_read_raw(struct iio_dev *iio_device,
 			   struct iio_chan_spec const *chan, int *val,
 			   int *val2, long mask)
 {
-	int ret;
 	struct ltr390_data *data = iio_priv(iio_device);
+	int ret;
 
+	guard(mutex)(&data->lock);
 	switch (mask) {
 	case IIO_CHAN_INFO_RAW:
 		ret = ltr390_register_read(data, LTR390_UVS_DATA);
 		if (ret < 0)
 			return ret;
+
 		*val = ret;
 		return IIO_VAL_INT;
+
 	case IIO_CHAN_INFO_SCALE:
 		*val = LTR390_WINDOW_FACTOR;
 		*val2 = LTR390_COUNTS_PER_UVI;
 		return IIO_VAL_FRACTIONAL;
+
+	case IIO_CHAN_INFO_INT_TIME:
+		*val = data->int_time_us;
+		return IIO_VAL_INT;
+
 	default:
 		return -EINVAL;
 	}
 }
 
-static const struct iio_info ltr390_info = {
-	.read_raw = ltr390_read_raw,
-};
+/* integration time in us */
+static const int ltr390_int_time_map_us[] = {400000, 200000, 100000, 50000, 25000, 12500};
+static const int ltr390_gain_map[] = {1, 3, 6, 9, 18};
 
 static const struct iio_chan_spec ltr390_channel = {
 	.type = IIO_UVINDEX,
-	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
+	.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE)
+};
+
+static int ltr390_set_gain(struct ltr390_data *data, int val)
+{
+	int ret, idx;
+
+	for (idx = 0; idx < ARRAY_SIZE(ltr390_gain_map); idx++) {
+		if (ltr390_gain_map[idx] != val)
+			continue;
+
+		guard(mutex)(&data->lock);
+		ret = regmap_update_bits(data->regmap,
+					LTR390_ALS_UVS_GAIN,
+					LTR390_ALS_UVS_GAIN_MASK, idx);
+		if (ret)
+			return ret;
+
+		data->gain = ltr390_gain_map[idx];
+		break;
+	}
+
+	return 0;
+}
+
+static int ltr390_set_int_time(struct ltr390_data *data, int val)
+{
+	int ret, idx;
+
+	for (idx = 0; idx < ARRAY_SIZE(ltr390_int_time_map_us); idx++) {
+		if (ltr390_int_time_map_us[idx] != val)
+			continue;
+
+		guard(mutex)(&data->lock);
+		ret = regmap_update_bits(data->regmap,
+					LTR390_ALS_UVS_MEAS_RATE,
+					LTR390_ALS_UVS_INT_TIME_MASK,
+					LTR390_ALS_UVS_INT_TIME(idx));
+		if (ret)
+			return ret;
+
+		data->int_time_us = ltr390_int_time_map_us[idx];
+		break;
+	}
+
+	return 0;
+}
+
+static int ltr390_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
+				const int **vals, int *type, int *length, long mask)
+{
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE:
+		*length = ARRAY_SIZE(ltr390_gain_map);
+		*type = IIO_VAL_INT;
+		*vals = ltr390_gain_map;
+		return IIO_AVAIL_LIST;
+	case IIO_CHAN_INFO_INT_TIME:
+		*length = ARRAY_SIZE(ltr390_int_time_map_us);
+		*type = IIO_VAL_INT;
+		*vals = ltr390_int_time_map_us;
+		return IIO_AVAIL_LIST;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ltr390_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
+				int val, int val2, long mask)
+{
+	struct ltr390_data *data = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE:
+		if (val2 != 0)
+			return -EINVAL;
+
+		return ltr390_set_gain(data, val);
+
+	case IIO_CHAN_INFO_INT_TIME:
+		if (val2 != 0)
+			return -EINVAL;
+
+		return ltr390_set_int_time(data, val);
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info ltr390_info = {
+	.read_raw = ltr390_read_raw,
+	.write_raw = ltr390_write_raw,
+	.read_avail = ltr390_read_avail,
 };
 
 static int ltr390_probe(struct i2c_client *client)
@@ -139,6 +249,11 @@  static int ltr390_probe(struct i2c_client *client)
 				     "regmap initialization failed\n");
 
 	data->client = client;
+	/* default value of integration time from pg: 15 of the datasheet */
+	data->int_time_us = 100000;
+	/* default value of gain from pg: 16 of the datasheet */
+	data->gain = 3;
+
 	mutex_init(&data->lock);
 
 	indio_dev->info = &ltr390_info;
@@ -162,7 +277,7 @@  static int ltr390_probe(struct i2c_client *client)
 	usleep_range(1000, 2000);
 
 	ret = regmap_set_bits(data->regmap, LTR390_MAIN_CTRL,
-			      LTR390_SENSOR_ENABLE | LTR390_UVS_MODE);
+				LTR390_SENSOR_ENABLE | LTR390_UVS_MODE);
 	if (ret)
 		return dev_err_probe(dev, ret, "failed to enable the sensor\n");
 
@@ -189,6 +304,7 @@  static struct i2c_driver ltr390_driver = {
 	.probe = ltr390_probe,
 	.id_table = ltr390_id,
 };
+
 module_i2c_driver(ltr390_driver);
 
 MODULE_AUTHOR("Anshul Dalal <anshulusr@gmail.com>");