lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <4dbcd233-cdd7-49ad-8db6-4bd3d706e82d@gmail.com>
Date:   Sun, 19 Nov 2023 06:08:25 +0100
From:   Javier Carrasco <javier.carrasco.cruz@...il.com>
To:     Jonathan Cameron <jic23@...nel.org>,
        Lars-Peter Clausen <lars@...afoo.de>,
        Rob Herring <robh+dt@...nel.org>,
        Krzysztof Kozlowski <krzysztof.kozlowski+dt@...aro.org>,
        Conor Dooley <conor+dt@...nel.org>
Cc:     linux-kernel@...r.kernel.org, linux-iio@...r.kernel.org,
        devicetree@...r.kernel.org
Subject: Re: [PATCH 1/2] iio: light: add VEML6075 UVA and UVB light sensor
 driver



On 19.11.23 05:58, Javier Carrasco wrote:
> The Vishay VEMl6075 is a low power, 16-bit resolution UVA and UVB
> light sensor with I2C interface and noise compensation (visible and
> infrarred).
> 
> Every UV channel generates an output measured in counts per integration
> period. Available integration times are 50 ms, 100 ms, 200 ms, 400 ms
> and 800 ms,
> 
> This driver adds support for both UV channels and the ultraviolet
> index (UVI) inferred from them according to the device application note
> with open-air (no teflon) coefficients.
> 
> Signed-off-by: Javier Carrasco <javier.carrasco.cruz@...il.com>
> ---
>  MAINTAINERS                  |   6 +
>  drivers/iio/light/Kconfig    |  11 +
>  drivers/iio/light/Makefile   |   1 +
>  drivers/iio/light/veml6075.c | 503 +++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 521 insertions(+)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 97f51d5ec1cf..2f13a5088d41 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -23184,6 +23184,12 @@ S:	Maintained
>  F:	drivers/input/serio/userio.c
>  F:	include/uapi/linux/userio.h
>  
> +VISHAY VEML6075 UVA AND UVB LIGHT SENSOR DRIVER
> +M:	Javier Carrasco <javier.carrasco.cruz@...il.com>
> +S:	Maintained
> +F:	Documentation/devicetree/bindings/iio/light/veml6075.yaml
> +F:	drivers/iio/light/veml6075.c
> +
>  VISL VIRTUAL STATELESS DECODER DRIVER
>  M:	Daniel Almeida <daniel.almeida@...labora.com>
>  L:	linux-media@...r.kernel.org
> diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
> index 45edba797e4c..f68e62196bc2 100644
> --- a/drivers/iio/light/Kconfig
> +++ b/drivers/iio/light/Kconfig
> @@ -637,6 +637,17 @@ config VEML6070
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called veml6070.
>  
> +config VEML6075
> +	tristate "VEML6075 UVA and UVB light sensor"
> +	select REGMAP_I2C
> +	depends on I2C
> +	help
> +	  Say Y here if you want to build a driver for the Vishay VEML6075 UVA
> +	  and UVB light sensor.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called veml6075.
> +
>  config VL6180
>  	tristate "VL6180 ALS, range and proximity sensor"
>  	depends on I2C
> diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
> index c0db4c4c36ec..c8289e24e3f6 100644
> --- a/drivers/iio/light/Makefile
> +++ b/drivers/iio/light/Makefile
> @@ -60,5 +60,6 @@ obj-$(CONFIG_VCNL4000)		+= vcnl4000.o
>  obj-$(CONFIG_VCNL4035)		+= vcnl4035.o
>  obj-$(CONFIG_VEML6030)		+= veml6030.o
>  obj-$(CONFIG_VEML6070)		+= veml6070.o
> +obj-$(CONFIG_VEML6075)		+= veml6075.o
>  obj-$(CONFIG_VL6180)		+= vl6180.o
>  obj-$(CONFIG_ZOPT2201)		+= zopt2201.o
> diff --git a/drivers/iio/light/veml6075.c b/drivers/iio/light/veml6075.c
> new file mode 100644
> index 000000000000..b7d9319c3906
> --- /dev/null
> +++ b/drivers/iio/light/veml6075.c
> @@ -0,0 +1,503 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * veml6075.c - Support for Vishay VEML6075 UVA and UVB light sensor
> + *
> + * Copyright 2023 Javier Carrasco <javier.carrasco.cruz@...il.com>
> + *
> + * IIO driver for VEML6075 (7-bit I2C slave address 0x10)
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/regmap.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +
> +#define VEML6075_DRIVER_NAME "veml6075"
> +
> +#define VEML6075_CMD_CONF	0x00 /* configuration register */
> +#define VEML6075_CMD_UVA	0x07 /* UVA channel */
> +#define VEML6075_CMD_UVB	0x09 /* UVB channel */
> +#define VEML6075_CMD_COMP1	0x0A /* visible light compensation */
> +#define VEML6075_CMD_COMP2	0x0B /* infrarred light compensation */
> +#define VEML6075_CMD_ID		0x0C /* device ID */
> +
> +#define VEML6075_CONF_IT	GENMASK(6, 4) /* intregration time */
> +#define VEML6075_CONF_HD	BIT(3) /* dynamic setting */
> +#define VEML6075_CONF_TRIG	BIT(2) /* trigger */
> +#define VEML6075_CONF_AF	BIT(1) /* active force enable */
> +#define VEML6075_CONF_SD	BIT(0) /* shutdown */
> +
> +#define VEML6075_CONF_IT_50	0x00 /* integration time 50 ms */
> +#define VEML6075_CONF_IT_100	0x01 /* integration time 100 ms */
> +#define VEML6075_CONF_IT_200	0x02 /* integration time 200 ms */
> +#define VEML6075_CONF_IT_400	0x03 /* integration time 400 ms */
> +#define VEML6075_CONF_IT_800	0x04 /* integration time 800 ms */
> +
> +/* Open-air coefficients and responsivity */
> +#define VEML6075_A_COEF		2220
> +#define VEML6075_B_COEF		1330
> +#define VEML6075_C_COEF		2950
> +#define VEML6075_D_COEF		1740
> +#define VEML6075_UVA_RESP	1461
> +#define VEML6075_UVB_RESP	2591
> +
> +static const int veml6075_it_ms[] = { 50, 100, 200, 400, 800 };
> +static const char veml6075_it_ms_avail[] = "50 100 200 400 800";
> +
> +struct veml6075_data {
> +	struct i2c_client *client;
> +	struct regmap *regmap;
> +	struct mutex lock; /* register access lock */
> +};
> +
> +/* channel number */
> +enum veml6075_chan {
> +	CH_UVA,
> +	CH_UVB,
> +};
> +
> +static const struct iio_chan_spec veml6075_channels[] = {
> +	{
> +		.type = IIO_INTENSITY,
> +		.channel = CH_UVA,
> +		.modified = 1,
> +		.channel2 = IIO_MOD_LIGHT_UV,
> +		.extend_name = "UVA",
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +			BIT(IIO_CHAN_INFO_SCALE),
> +		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
> +	},
> +	{
> +		.type = IIO_INTENSITY,
> +		.channel = CH_UVB,
> +		.modified = 1,
> +		.channel2 = IIO_MOD_LIGHT_UV,
> +		.extend_name = "UVB",
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +			BIT(IIO_CHAN_INFO_SCALE),
> +		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
> +	},
> +	{
> +		.type = IIO_UVINDEX,
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
> +		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
> +	},
> +};
> +
> +static IIO_CONST_ATTR_INT_TIME_AVAIL(veml6075_it_ms_avail);
> +
> +static struct attribute *veml6075_attributes[] = {
> +	&iio_const_attr_integration_time_available.dev_attr.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group veml6075_attribute_group = {
> +	.attrs = veml6075_attributes,
> +};
> +
> +static int veml6075_shutdown(struct veml6075_data *data)
> +{
> +	return regmap_update_bits(data->regmap, VEML6075_CMD_CONF,
> +				  VEML6075_CONF_SD, VEML6075_CONF_SD);
> +}
> +
> +static int veml6075_request_measurement(struct veml6075_data *data)
> +{
> +	int ret, conf, int_time;
> +
> +	ret = regmap_read(data->regmap, VEML6075_CMD_CONF, &conf);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* disable shutdown and trigger measurement */
> +	ret = regmap_write(data->regmap, VEML6075_CMD_CONF,
> +			   (conf | VEML6075_CONF_TRIG) & ~VEML6075_CONF_SD);
> +	if (ret < 0)
> +		return ret;
> +
> +	/*
> +	 * A measurement requires between 1.30 and 1.40 times the integration
> +	 * time for all possible configurations. Using a 1.50 factor simplifies
> +	 * operations and ensures reliability under all circumstances.
> +	 */
> +	int_time = veml6075_it_ms[FIELD_GET(VEML6075_CONF_IT, conf)];
> +	msleep(int_time + (int_time / 2));
> +
> +	/* shutdown again, data registers are still accessible */
> +	return veml6075_shutdown(data);
> +}
> +
> +static int veml6075_uva_comp(int raw_uva, int comp1, int comp2)
> +{
> +	int comp1a_c, comp2a_c, uva_comp;
> +
> +	comp1a_c = (comp1 * VEML6075_A_COEF) / 1000U;
> +	comp2a_c = (comp2 * VEML6075_B_COEF) / 1000U;
> +	uva_comp = raw_uva - comp1a_c - comp2a_c;
> +	pr_err("JCC: uva=%d, c1=%d, c2=%d, c1ca=%d, c2ca=%d, uvac=%d\n",
> +	       raw_uva, comp1, comp2, comp1a_c, comp2a_c, uva_comp);
Obviously this debug message should be gone and it will be removed for v2.
> +
> +	return clamp_val(uva_comp, 0, U16_MAX);
> +}
> +
> +static int veml6075_uvb_comp(int raw_uvb, int comp1, int comp2)
> +{
> +	int comp1b_c, comp2b_c, uvb_comp;
> +
> +	comp1b_c = (comp1 * VEML6075_C_COEF) / 1000U;
> +	comp2b_c = (comp2 * VEML6075_D_COEF) / 1000U;
> +	uvb_comp = raw_uvb - comp1b_c - comp2b_c;
> +	pr_err("JCC: uvb=%d, c1=%d, c2=%d, c1cb=%d, c2cb=%d, uvbc=%d\n",
> +	       raw_uvb, comp1, comp2, comp1b_c, comp2b_c, uvb_comp);
Same here.
> +
> +	return clamp_val(uvb_comp, 0, U16_MAX);
> +}
> +
> +static int veml6075_read_comp(struct veml6075_data *data, int *c1, int *c2)
> +{
> +	int ret;
> +
> +	ret = regmap_read(data->regmap, VEML6075_CMD_COMP1, c1);
> +	if (ret < 0)
> +		return ret;
> +
> +	return regmap_read(data->regmap, VEML6075_CMD_COMP2, c2);
> +}
> +
> +static int veml6075_read_uva_count(struct veml6075_data *data, int *uva)
> +{
> +	return regmap_read(data->regmap, VEML6075_CMD_UVA, uva);
> +}
> +
> +static int veml6075_read_uvb_count(struct veml6075_data *data, int *uvb)
> +{
> +	return regmap_read(data->regmap, VEML6075_CMD_UVB, uvb);
> +}
> +
> +static int veml6075_read_uv_direct(struct veml6075_data *data, int chan,
> +				   int *val)
> +{
> +	int c1, c2, ret;
> +
> +	ret = veml6075_request_measurement(data);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = veml6075_read_comp(data, &c1, &c2);
> +	if (ret < 0)
> +		return ret;
> +
> +	switch (chan) {
> +	case CH_UVA:
> +		ret = veml6075_read_uva_count(data, val);
> +		if (ret < 0)
> +			return ret;
> +
> +		*val = veml6075_uva_comp(*val, c1, c2);
> +		break;
> +	case CH_UVB:
> +		ret = veml6075_read_uvb_count(data, val);
> +		if (ret < 0)
> +			return ret;
> +
> +		*val = veml6075_uvb_comp(*val, c1, c2);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return IIO_VAL_INT;
> +}
> +
> +static int veml6075_read_int_time_index(struct veml6075_data *data)
> +{
> +	int ret, conf;
> +
> +	ret = regmap_read(data->regmap, VEML6075_CMD_CONF, &conf);
> +	if (ret < 0)
> +		return ret;
> +
> +	return FIELD_GET(VEML6075_CONF_IT, conf);
> +}
> +
> +static int veml6075_read_int_time_ms(struct veml6075_data *data, int *val)
> +{
> +	int int_index;
> +
> +	int_index = veml6075_read_int_time_index(data);
> +	if (int_index < 0)
> +		return int_index;
> +
> +	*val = veml6075_it_ms[int_index];
> +
> +	return IIO_VAL_INT;
> +}
> +
> +static int veml6075_get_uvi_micro(struct veml6075_data *data, int uva_comp,
> +				  int uvb_comp)
> +{
> +	int uvia_micro = uva_comp * VEML6075_UVA_RESP;
> +	int uvib_micro = uvb_comp * VEML6075_UVB_RESP;
> +	int int_index;
> +
> +	int_index = veml6075_read_int_time_index(data);
> +	if (int_index < 0)
> +		return int_index;
> +
> +	switch (int_index) {
> +	case VEML6075_CONF_IT_50:
> +		return uvia_micro + uvib_micro;
> +	case VEML6075_CONF_IT_100:
> +	case VEML6075_CONF_IT_200:
> +	case VEML6075_CONF_IT_400:
> +	case VEML6075_CONF_IT_800:
> +		return (uvia_micro + uvib_micro) / (2 << int_index);
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int veml6075_read_uvi(struct veml6075_data *data, int *val, int *val2)
> +{
> +	int ret, c1, c2, uva, uvb, uvi_micro;
> +
> +	ret = veml6075_request_measurement(data);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = veml6075_read_comp(data, &c1, &c2);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = veml6075_read_uva_count(data, &uva);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = veml6075_read_uvb_count(data, &uvb);
> +	if (ret < 0)
> +		return ret;
> +
> +	uvi_micro = veml6075_get_uvi_micro(data, veml6075_uva_comp(uva, c1, c2),
> +					   veml6075_uvb_comp(uvb, c1, c2));
> +	if (uvi_micro < 0)
> +		return uvi_micro;
> +
> +	*val = uvi_micro / 1000000LL;
> +	*val2 = uvi_micro % 1000000LL;
> +
> +	return IIO_VAL_INT_PLUS_MICRO;
> +}
> +
> +static int veml6075_read_responsivity(int chan, int *val, int *val2)
> +{
> +	/* scale = 1 / resp */
> +	switch (chan) {
> +	case CH_UVA:
> +		/* resp = 0.93 c/uW/cm2: scale = 1.75268817 */
> +		*val = 1;
> +		*val2 = 75268817;
> +		break;
> +	case CH_UVB:
> +		/* resp = 2.1 c/uW/cm2: scale = 0.476190476 */
> +		*val = 0;
> +		*val2 = 476190476;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return IIO_VAL_INT_PLUS_NANO;
> +}
> +
> +static int veml6075_read_raw(struct iio_dev *indio_dev,
> +			     struct iio_chan_spec const *chan,
> +			     int *val, int *val2, long mask)
> +{
> +	struct veml6075_data *data = iio_priv(indio_dev);
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		mutex_lock(&data->lock);
> +		ret = veml6075_read_uv_direct(data, chan->channel, val);
> +		break;
> +	case IIO_CHAN_INFO_PROCESSED:
> +		mutex_lock(&data->lock);
> +		ret = veml6075_read_uvi(data, val, val2);
> +		break;
> +	case IIO_CHAN_INFO_INT_TIME:
> +		mutex_lock(&data->lock);
> +		ret = veml6075_read_int_time_ms(data, val);
> +		break;
> +	case IIO_CHAN_INFO_SCALE:
> +		return veml6075_read_responsivity(chan->channel, val, val2);
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	mutex_unlock(&data->lock);
> +
> +	return ret;
> +}
> +
> +static int veml6075_write_int_time_ms(struct veml6075_data *data, int val)
> +{
> +	int conf, i = ARRAY_SIZE(veml6075_it_ms);
> +
> +	while (i-- > 0) {
> +		if (val == veml6075_it_ms[i])
> +			break;
> +	}
> +	if (i < 0)
> +		return -EINVAL;
> +
> +	conf = FIELD_PREP(VEML6075_CONF_IT, i);
> +
> +	return regmap_update_bits(data->regmap, VEML6075_CMD_CONF,
> +				  VEML6075_CONF_IT, conf);
> +}
> +
> +static int veml6075_write_raw(struct iio_dev *indio_dev,
> +			      struct iio_chan_spec const *chan,
> +			      int val, int val2, long mask)
> +{
> +	struct veml6075_data *data = iio_priv(indio_dev);
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_INT_TIME:
> +		mutex_lock(&data->lock);
> +		ret = veml6075_write_int_time_ms(data, val);
> +		mutex_unlock(&data->lock);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return ret;
> +}
> +
> +static const struct iio_info veml6075_info = {
> +	.read_raw = veml6075_read_raw,
> +	.write_raw = veml6075_write_raw,
> +	.attrs = &veml6075_attribute_group,
> +};
> +
> +static bool veml6075_readable_reg(struct device *dev, unsigned int reg)
> +{
> +	switch (reg) {
> +	case VEML6075_CMD_CONF:
> +	case VEML6075_CMD_UVA:
> +	case VEML6075_CMD_UVB:
> +	case VEML6075_CMD_COMP1:
> +	case VEML6075_CMD_COMP2:
> +	case VEML6075_CMD_ID:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool veml6075_writable_reg(struct device *dev, unsigned int reg)
> +{
> +	switch (reg) {
> +	case VEML6075_CMD_CONF:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static const struct regmap_config veml6075_regmap_config = {
> +	.name = VEML6075_DRIVER_NAME,
> +	.reg_bits = 8,
> +	.val_bits = 16,
> +	.max_register = VEML6075_CMD_ID,
> +	.readable_reg = veml6075_readable_reg,
> +	.writeable_reg = veml6075_writable_reg,
> +	.val_format_endian = REGMAP_ENDIAN_LITTLE,
> +
> +};
> +
> +static int veml6075_probe(struct i2c_client *client)
> +{
> +	struct veml6075_data *data;
> +	struct iio_dev *indio_dev;
> +	struct regmap *regmap;
> +	int config, ret;
> +
> +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	regmap = devm_regmap_init_i2c(client, &veml6075_regmap_config);
> +	if (IS_ERR(regmap))
> +		return PTR_ERR(regmap);
> +
> +	data = iio_priv(indio_dev);
> +	i2c_set_clientdata(client, indio_dev);
> +	data->client = client;
> +	data->regmap = regmap;
> +
> +	mutex_init(&data->lock);
> +
> +	indio_dev->name = VEML6075_DRIVER_NAME;
> +	indio_dev->info = &veml6075_info;
> +	indio_dev->channels = veml6075_channels;
> +	indio_dev->num_channels = ARRAY_SIZE(veml6075_channels);
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	ret = devm_regulator_get_enable_optional(&client->dev, "vdd");
> +	if (ret < 0 && ret != -ENODEV)
> +		return ret;
> +
> +	/* default: 100ms integration time, active force enable, shutdown */
> +	config = FIELD_PREP(VEML6075_CONF_IT, VEML6075_CONF_IT_100) |
> +		VEML6075_CONF_AF | VEML6075_CONF_SD;
> +	ret = regmap_write(data->regmap, VEML6075_CMD_CONF, config);
> +	if (ret < 0)
> +		return ret;
> +
> +	return iio_device_register(indio_dev);
> +}
> +
> +static void veml6075_remove(struct i2c_client *client)
> +{
> +	struct iio_dev *indio_dev = i2c_get_clientdata(client);
> +
> +	iio_device_unregister(indio_dev);
> +}
> +
> +static const struct i2c_device_id veml6075_id[] = {
> +	{ "veml6075", 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, veml6075_id);
> +
> +static const struct of_device_id veml6075_of_match[] = {
> +	{ .compatible = "vishay,veml6075" },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, veml6075_of_match);
> +
> +static struct i2c_driver veml6075_driver = {
> +	.driver = {
> +		.name   = VEML6075_DRIVER_NAME,
> +		.of_match_table = veml6075_of_match,
> +	},
> +	.probe = veml6075_probe,
> +	.remove  = veml6075_remove,
> +	.id_table = veml6075_id,
> +};
> +
> +module_i2c_driver(veml6075_driver);
> +
> +MODULE_AUTHOR("Javier Carrasco <javier.carrasco.cruz@...il.com>");
> +MODULE_DESCRIPTION("Vishay VEML6075 UVA and UVB light sensor driver");
> +MODULE_LICENSE("GPL");
> 
Best regards,
Javier Carrasco

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ