All of lore.kernel.org
 help / color / mirror / Atom feed
From: Matti Vaittinen <mazziesaccount@gmail.com>
To: Subhajit Ghosh <subhajit.ghosh@tweaklogic.com>,
	Jonathan Cameron <jic23@kernel.org>,
	Lars-Peter Clausen <lars@metafoo.de>,
	Rob Herring <robh+dt@kernel.org>,
	Krzysztof Kozlowski <krzysztof.kozlowski+dt@linaro.org>,
	Conor Dooley <conor+dt@kernel.org>,
	Andy Shevchenko <andriy.shevchenko@linux.intel.com>,
	Paul Gazzillo <paul@pgazz.com>
Cc: Matt Ranostay <matt@ranostay.sg>,
	Stefan Windfeldt-Prytz <stefan.windfeldt-prytz@axis.com>,
	linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH v1 2/2] iio: light: Add support for APDS9306 Light Sensor
Date: Fri, 27 Oct 2023 13:49:07 +0300	[thread overview]
Message-ID: <341cb934-2e91-442a-a4db-2f69f79ad0b6@gmail.com> (raw)
In-Reply-To: <20231026143532.39660-3-subhajit.ghosh@tweaklogic.com>

Hi Subhajit,

It's nice to see the GTS helpers are used (and hopefully helpful). I 
didn't have the time to go through everything with full focus - so 
please just tell me if some of my questions are silly :) The comments I 
marked as 'nit' aren't really important - feel free to use your 
judgement on them :)

On 10/26/23 17:35, Subhajit Ghosh wrote:
> Driver support for Avago (Broadcom) APDS9306 Ambient Light Sensor with als
> and clear channels with i2c interface. Hardware interrupt configuration is
> optional. It is a low power device with 20 bit resolution and has
> configurable adaptive interrupt mode and interrupt persistence mode.
> The device also features inbuilt hardware gain, multiple integration time
> selection options and sampling frequency selection options.
> 
> v0 -> v1
> - Fixed errors as per previous review
> - Longer commit messages and descriptions
> - Updated scale calculations as per iio gts scheme to export proper scale
>    values and tables to userspace
> - Removed processed attribute for the same channel for which raw is
>    provided, instead, exporting proper scale and scale table to userspace so
>    that userspace can do "(raw + offset) * scale" and derive Lux values
> - Fixed IIO attribute range syntax
> - Keeping the regmap lock enabled as the driver uses unlocked regfield
>    accesses from interrupt handler
> - Several levels of cleanups by placing guard mutexes in proper places and
>    returning immediately in case of an error
> - Using iio_device_claim_direct_mode() during raw reads so that
>    configurations could not be changed during an adc conversion period
> - In case of a powerdown error, returning immediately
> - Removing the definition of direction of the hardware interrupt and
>    leaving it on to device tree
> - Adding the powerdown callback after doing device initialization
> - Removed the regcache_cache_only() implementation
> 
> Signed-off-by: Subhajit Ghosh <subhajit.ghosh@tweaklogic.com>
> ---
>   drivers/iio/light/Kconfig    |   12 +
>   drivers/iio/light/Makefile   |    1 +
>   drivers/iio/light/apds9306.c | 1334 ++++++++++++++++++++++++++++++++++
>   3 files changed, 1347 insertions(+)
>   create mode 100644 drivers/iio/light/apds9306.c
> 
> diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
> index 45edba797e4c..04e7d10f1470 100644
> --- a/drivers/iio/light/Kconfig
> +++ b/drivers/iio/light/Kconfig
> @@ -73,6 +73,18 @@ config APDS9300
>   	  To compile this driver as a module, choose M here: the
>   	  module will be called apds9300.
>   
> +config APDS9306
> +	tristate "Avago APDS9306 Ambient Light Sensor"
> +	depends on I2C
> +	select REGMAP_I2C
> +	select IIO_GTS_HELPER
> +	help
> +	  If you say Y or M here, you get support for Avago APDS9306
> +	  Ambient Light Sensor.
> +
> +	  If built as a dynamically linked module, it will be called
> +	  apds9306.
> +
>   config APDS9960
>   	tristate "Avago APDS9960 gesture/RGB/ALS/proximity sensor"
>   	select REGMAP_I2C
> diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
> index c0db4c4c36ec..ab94eac04db0 100644
> --- a/drivers/iio/light/Makefile
> +++ b/drivers/iio/light/Makefile
> @@ -10,6 +10,7 @@ obj-$(CONFIG_ADUX1020)		+= adux1020.o
>   obj-$(CONFIG_AL3010)		+= al3010.o
>   obj-$(CONFIG_AL3320A)		+= al3320a.o
>   obj-$(CONFIG_APDS9300)		+= apds9300.o
> +obj-$(CONFIG_APDS9306)		+= apds9306.o
>   obj-$(CONFIG_APDS9960)		+= apds9960.o
>   obj-$(CONFIG_AS73211)		+= as73211.o
>   obj-$(CONFIG_BH1750)		+= bh1750.o
> diff --git a/drivers/iio/light/apds9306.c b/drivers/iio/light/apds9306.c
> new file mode 100644
> index 000000000000..352893913a29
> --- /dev/null
> +++ b/drivers/iio/light/apds9306.c
> @@ -0,0 +1,1334 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * APDS-9306/APDS-9306-065 Ambient Light Sensor
> + * I2C Address: 0x52
> + * Datasheet: https://docs.broadcom.com/doc/AV02-4755EN
> + *
> + * Copyright (C) 2023 Subhajit Ghosh <subhajit.ghosh@tweaklogic.com>
> + */
> +

...

> +
> +/**
> + * struct part_id_gts_multiplier - Part no. & corresponding gts multiplier
> + * @part_id: Part ID of the device
> + * @max_scale_int: Multiplier for iio_init_iio_gts()
> + * @max_scale_nano: Multiplier for iio_init_iio_gts()
> + */
> +struct part_id_gts_multiplier {
> +	int part_id;
> +	int max_scale_int;
> +	int max_scale_nano;
> +};
> +
> +/*
> + * As per the datasheet, at HW Gain = 3x, Integration time 100mS (32x),
> + * typical 2000 ADC counts are observed for 49.8 uW per sq cm (340.134 lux)
> + * for apds9306 and 43 uW per sq cm (293.69 lux) for apds9306-065.
> + * Assuming lux per count is linear across all integration time ranges.

I love this comment. Still, even with this I managed to get confused :) 
It might be beneficial to mention that the minimum gain is 1x from both 
the integration time and gain. (Not mandatory, I was just trying to 
figure out why this was so difficult for me to follow).

> + * Lux = (raw + offset) * scale; offset can be any value by userspace.
> + * HG = Hardware Gain; ITG = Gain by changing integration time.
> + * Scale table by IIO GTS Helpers = (1 / HG) * (1 / ITG) * Multiplier.
> + *
> + * The Lux values provided in the datasheet are at ITG=32x and HG=3x,
> + * at typical 2000 count.
> + *
> + * Lux per ADC count at 3x and 32x for apds9306 = 340.134 / 2000
> + * Lux per ADC count at 3x and 32x for apds9306-065 = 293.69 / 2000
> + *
> + * The Multiplier for the scale table provided to userspace:
> + * IIO GTS scale Multiplier for apds9306 = (340.134 / 2000) * 32 * 3

'nit'
Could you please show also the result of the computation (16.326432)...

> + * IIO GTS scale Multiplier for apds9306-065 = (293.69 / 2000) * 32 * 3
> + */
> +static struct part_id_gts_multiplier apds9306_gts_mul[] = {
> +	{
> +		.part_id = 0xB1,
> +		.max_scale_int = 16,
> +		.max_scale_nano = 3264320,

'nit'
... to make it easier to see what we have here corresponds to the values 
in comment above.

> +	}, {
> +		.part_id = 0xB3,
> +		.max_scale_int = 14,
> +		.max_scale_nano = 9712000,
> +	},
> +};
> +
> +/**
> + * apds9306_repeat_rate_freq - Sampling Frequency in uHz
> + */
> +static const int apds9306_repeat_rate_freq[][2] = {
> +	{40, 0},
> +	{20, 0},
> +	{10, 0},
> +	{5,  0},
> +	{2,  0},
> +	{1,  0},
> +	{0, 500000},
> +};
> +
> +/**
> + * apds9306_repeat_rate_period - Sampling period in uSec
> + */
> +static const int apds9306_repeat_rate_period[] = {
> +	25000, 50000, 100000, 200000, 500000, 1000000, 2000000
> +};
> +static_assert(ARRAY_SIZE(apds9306_repeat_rate_freq) ==
> +	      ARRAY_SIZE(apds9306_repeat_rate_period));
> +
> +/**
> + * struct apds9306_data - apds9306 private data and registers definitions
> + *
> + * All regfield definitions are named exactly according to datasheet for easy
> + * search
> + *
> + * @dev:	Pointer to the device structure
> + * @gts:	IIO Gain Time Scale structure
> + * @mutex:	Lock for protecting register access, adc reads and power
> + * @regmap:	Regmap structure pointer
> + * @regfield_sw_reset:	Reg: MAIN_CTRL, Field: SW_Reset
> + * @regfield_en:	Reg: MAIN_CTRL, Field: ALS_EN
> + * @regfield_intg_time:	Reg: ALS_MEAS_RATE, Field: ALS Resolution/Bit Width
> + * @regfield_repeat_rate:	Reg: ALS_MEAS_RATE, Field: ALS Measurement Rate
> + * @regfield_scale:	Reg: ALS_GAIN, Field: ALS Gain Range
> + * @regfield_int_src:	Reg: INT_CFG, Field: ALS Interrupt Source
> + * @regfield_int_thresh_var_en:	Reg: INT_CFG, Field: ALS Var Interrupt Mode
> + * @regfield_int_en:	Reg: INT_CFG, Field: ALS Interrupt Enable
> + * @regfield_int_persist_val:	Reg: INT_PERSISTENCE, Field: ALS_PERSIST
> + * @regfield_int_thresh_var_val:	Reg: ALS_THRSH_VAR, Field: ALS_THRES_VAR
> + * @nlux_per_count:	nano lux per ADC count for a particular model
> + * @read_data_available:	Flag set by IRQ handler for ADC data available
> + * @intg_time_idx:	Array index for integration times
> + * @repeat_rate_idx:	Array index for sampling frequency
> + * @gain_idx:	Array index for gain
> + * @int_ch:	Currently selected Interrupt channel
> + */
> +struct apds9306_data {
> +	struct device *dev;
> +	struct iio_gts gts;
> +	/*
> +	 * Protects device settings changes where some calculations are required
> +	 * before or after setting or getting the raw settings values from regmap
> +	 * writes or reads respectively.
> +	 */
> +	struct mutex mutex;
> +
> +	struct regmap *regmap;
> +	struct regmap_field *regfield_sw_reset;
> +	struct regmap_field *regfield_en;
> +	struct regmap_field *regfield_intg_time;
> +	struct regmap_field *regfield_repeat_rate;
> +	struct regmap_field *regfield_scale;
> +	struct regmap_field *regfield_int_src;
> +	struct regmap_field *regfield_int_thresh_var_en;
> +	struct regmap_field *regfield_int_en;
> +	struct regmap_field *regfield_int_persist_val;
> +	struct regmap_field *regfield_int_thresh_var_val;
> +
> +	int nlux_per_count;
> +	int read_data_available;
> +	u8 intg_time_idx;
> +	u8 repeat_rate_idx;
> +	u8 gain_idx;

'nit'
I'm not sure caching the time and gain idx in the driver data is that 
beneficial? I assume you use regmap cache amyways. For me caching these 
add a bit of complexity when trying to ensure they are not used 
'uninitialized' for not that obvious benefit.

> +	u8 int_ch;
> +};
> +
> +/*
> + * Available scales with gain 1x - 18x, timings 3.125, 25, 50, 100, 200,
> + * 400 mS
> + * Time impacts to gain: 1x, 8x, 16x, 32x, 64x, 128x > + */
> +
> +#define APDS9306_GSEL_1X	0x00
> +#define APDS9306_GSEL_3X	0x01
> +#define APDS9306_GSEL_6X	0x02
> +#define APDS9306_GSEL_9X	0x03
> +#define APDS9306_GSEL_18X	0x04
> +
> +static const struct iio_gain_sel_pair apds9306_gains[] = {
> +	GAIN_SCALE_GAIN(1, APDS9306_GSEL_1X),
> +	GAIN_SCALE_GAIN(3, APDS9306_GSEL_3X),
> +	GAIN_SCALE_GAIN(6, APDS9306_GSEL_6X),
> +	GAIN_SCALE_GAIN(9, APDS9306_GSEL_9X),
> +	GAIN_SCALE_GAIN(18, APDS9306_GSEL_18X),
> +};
> +
> +#define APDS9306_MEAS_MODE_400MS	0x00
> +#define APDS9306_MEAS_MODE_200MS	0x01
> +#define APDS9306_MEAS_MODE_100MS	0x02
> +#define APDS9306_MEAS_MODE_50MS		0x03
> +#define APDS9306_MEAS_MODE_25MS		0x04
> +#define APDS9306_MEAS_MODE_3125US	0x05
> +
> +static const struct iio_itime_sel_mul apds9306_itimes[] = {
> +	GAIN_SCALE_ITIME_US(400000, APDS9306_MEAS_MODE_400MS, 128),
> +	GAIN_SCALE_ITIME_US(200000, APDS9306_MEAS_MODE_200MS, 64),
> +	GAIN_SCALE_ITIME_US(100000, APDS9306_MEAS_MODE_100MS, 32),
> +	GAIN_SCALE_ITIME_US(50000, APDS9306_MEAS_MODE_50MS, 16),
> +	GAIN_SCALE_ITIME_US(25000, APDS9306_MEAS_MODE_25MS, 8),
> +	GAIN_SCALE_ITIME_US(3125, APDS9306_MEAS_MODE_3125US, 1),
> +};
> +

...

> +
> +static const struct regmap_config apds9306_regmap = {
> +	.name = "apds9306_regmap",
> +	.reg_bits = 8,
> +	.val_bits = 8,
> +	.rd_table = &apds9306_readable_table,
> +	.wr_table = &apds9306_writable_table,
> +	.volatile_table = &apds9306_volatile_table,
> +	.precious_table = &apds9306_precious_table,
> +	.max_register = APDS9306_ALS_THRES_VAR,
> +	.cache_type = REGCACHE_RBTREE,
> +	/*
> +	 * Leaving the regmap lock enabled as regfield accesses are everywhere
> +	 * which are read modify writes and data mutex is not used in the
> +	 * interrupt handler.
> +	 */

To my eye this comment looks a bit misplaced without the
	.disable_locking = false,
- which is a no-op here. I think what you wrote in the comment is true 
(default assumption) for many drivers - maybe the comment is not needed?

> +};

...

> +static int apds9306_read_raw(struct iio_dev *indio_dev,
> +			     struct iio_chan_spec const *chan, int *val,
> +			     int *val2, long mask)
> +{
> +	struct apds9306_data *data = iio_priv(indio_dev);
> +	int ret, reg;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		if (chan->channel2 == IIO_MOD_LIGHT_CLEAR)
> +			reg = APDS9306_CLEAR_DATA_0;
> +		else
> +			reg = APDS9306_ALS_DATA_0;
> +		/*
> +		 * Changing device parameters during adc operation, resets
> +		 * the ADC which has to avoided.
> +		 */

Would you need to grab the mutex here? I think you want also prevent 
changing gain/time during the computations.

> +		ret = iio_device_claim_direct_mode(indio_dev);
> +		if (ret)
> +			return ret;
> +		ret = apds9306_read_data(data, val, reg);
> +		iio_device_release_direct_mode(indio_dev);
> +		if (ret)
> +			return ret;
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_INT_TIME:
> +		ret = apds9306_intg_time_get(data, val2);
> +		if (ret)
> +			return ret;
> +		*val = 0;
> +		return IIO_VAL_INT_PLUS_MICRO;
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		ret = apds9306_sampling_freq_get(data, val, val2);
> +		if (ret)
> +			return ret;
> +		return IIO_VAL_INT_PLUS_MICRO;
> +	case IIO_CHAN_INFO_SCALE:
> +		ret = apds9306_scale_get(data, val, val2);
> +		if (ret)
> +			return ret;
> +		return IIO_VAL_INT_PLUS_NANO;
> +	default:
> +		return -EINVAL;
> +	}
> +};
> +

Thanks for the nice driver!

Yours,
	-- Matti


-- 
Matti Vaittinen
Linux kernel developer at ROHM Semiconductors
Oulu Finland

~~ When things go utterly wrong vim users can always type :help! ~~



  parent reply	other threads:[~2023-10-27 10:49 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-10-26 14:35 [PATCH v1 0/2] Support for Avago APDS9306 Ambient Light Sensor Subhajit Ghosh
2023-10-26 14:35 ` [PATCH v1 1/2] dt-bindings: iio: light: Avago APDS9306 Subhajit Ghosh
2023-10-27 14:26   ` Conor Dooley
2023-10-26 14:35 ` [PATCH v1 2/2] iio: light: Add support for APDS9306 Light Sensor Subhajit Ghosh
2023-10-27  3:26   ` kernel test robot
2023-10-27 10:49   ` Matti Vaittinen [this message]
2023-10-27 11:21     ` Subhajit Ghosh
2023-10-27 12:34   ` Andy Shevchenko
2023-10-27 12:59     ` Subhajit Ghosh
2023-10-27 13:20       ` Subhajit Ghosh

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=341cb934-2e91-442a-a4db-2f69f79ad0b6@gmail.com \
    --to=mazziesaccount@gmail.com \
    --cc=andriy.shevchenko@linux.intel.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=jic23@kernel.org \
    --cc=krzysztof.kozlowski+dt@linaro.org \
    --cc=lars@metafoo.de \
    --cc=linux-iio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=matt@ranostay.sg \
    --cc=paul@pgazz.com \
    --cc=robh+dt@kernel.org \
    --cc=stefan.windfeldt-prytz@axis.com \
    --cc=subhajit.ghosh@tweaklogic.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.