All the mail mirrored from lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 1/2] iio: Add modifier for DUV light
@ 2018-07-19 20:26 Maxime Roussin-Bélanger
  2018-07-19 20:26 ` [PATCH v3 2/2] iio: light: introduce si1133 Maxime Roussin-Bélanger
  2018-07-21 16:15 ` [PATCH v3 1/2] iio: Add modifier for DUV light Jonathan Cameron
  0 siblings, 2 replies; 4+ messages in thread
From: Maxime Roussin-Bélanger @ 2018-07-19 20:26 UTC (permalink / raw
  To: Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler
  Cc: linux-iio, Jean-Francois Dagenais, Maxime Roussin-Bélanger

Signed-off-by: Maxime Roussin-Bélanger <maxime.roussinbelanger@gmail.com>
---
 Documentation/ABI/testing/sysfs-bus-iio | 4 +++-
 drivers/iio/industrialio-core.c         | 1 +
 include/uapi/linux/iio/types.h          | 1 +
 tools/iio/iio_event_monitor.c           | 2 ++
 4 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index fee35c00cc4e..23b213f87cf5 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -1263,13 +1263,15 @@ What:		/sys/.../iio:deviceX/in_intensityY_raw
 What:		/sys/.../iio:deviceX/in_intensityY_ir_raw
 What:		/sys/.../iio:deviceX/in_intensityY_both_raw
 What:		/sys/.../iio:deviceX/in_intensityY_uv_raw
+What:		/sys/.../iio:deviceX/in_intensityY_duv_raw
 KernelVersion:	3.4
 Contact:	linux-iio@vger.kernel.org
 Description:
 		Unit-less light intensity. Modifiers both and ir indicate
 		that measurements contains visible and infrared light
 		components or just infrared light, respectively. Modifier uv indicates
-		that measurements contain ultraviolet light components.
+		that measurements contain ultraviolet light components. Modifier duv
+		indicates that measurements contain deep ultraviolet light components.
 
 What:		/sys/.../iio:deviceX/in_uvindex_input
 KernelVersion:	4.6
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index fc340ed3dca1..66aba36d7fe1 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -104,6 +104,7 @@ static const char * const iio_modifier_names[] = {
 	[IIO_MOD_LIGHT_GREEN] = "green",
 	[IIO_MOD_LIGHT_BLUE] = "blue",
 	[IIO_MOD_LIGHT_UV] = "uv",
+	[IIO_MOD_LIGHT_DUV] = "duv",
 	[IIO_MOD_QUATERNION] = "quaternion",
 	[IIO_MOD_TEMP_AMBIENT] = "ambient",
 	[IIO_MOD_TEMP_OBJECT] = "object",
diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h
index 22e5e589a274..bf037a267c7d 100644
--- a/include/uapi/linux/iio/types.h
+++ b/include/uapi/linux/iio/types.h
@@ -80,6 +80,7 @@ enum iio_modifier {
 	IIO_MOD_CO2,
 	IIO_MOD_VOC,
 	IIO_MOD_LIGHT_UV,
+	IIO_MOD_LIGHT_DUV,
 };
 
 enum iio_event_type {
diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c
index d9b7e0f306c6..328c66d8a892 100644
--- a/tools/iio/iio_event_monitor.c
+++ b/tools/iio/iio_event_monitor.c
@@ -95,6 +95,7 @@ static const char * const iio_modifier_names[] = {
 	[IIO_MOD_LIGHT_GREEN] = "green",
 	[IIO_MOD_LIGHT_BLUE] = "blue",
 	[IIO_MOD_LIGHT_UV] = "uv",
+	[IIO_MOD_LIGHT_DUV] = "duv",
 	[IIO_MOD_QUATERNION] = "quaternion",
 	[IIO_MOD_TEMP_AMBIENT] = "ambient",
 	[IIO_MOD_TEMP_OBJECT] = "object",
@@ -176,6 +177,7 @@ static bool event_is_known(struct iio_event_data *event)
 	case IIO_MOD_LIGHT_GREEN:
 	case IIO_MOD_LIGHT_BLUE:
 	case IIO_MOD_LIGHT_UV:
+	case IIO_MOD_LIGHT_DUV:
 	case IIO_MOD_QUATERNION:
 	case IIO_MOD_TEMP_AMBIENT:
 	case IIO_MOD_TEMP_OBJECT:
-- 
2.18.0

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

* [PATCH v3 2/2] iio: light: introduce si1133
  2018-07-19 20:26 [PATCH v3 1/2] iio: Add modifier for DUV light Maxime Roussin-Bélanger
@ 2018-07-19 20:26 ` Maxime Roussin-Bélanger
  2018-07-21 16:06   ` Jonathan Cameron
  2018-07-21 16:15 ` [PATCH v3 1/2] iio: Add modifier for DUV light Jonathan Cameron
  1 sibling, 1 reply; 4+ messages in thread
From: Maxime Roussin-Bélanger @ 2018-07-19 20:26 UTC (permalink / raw
  To: Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler
  Cc: linux-iio, Jean-Francois Dagenais, Maxime Roussin-Bélanger

e-mail received from Silicon Lab to confirm that the licensing
isn't a problem.

"
Dear Maxime Roussin-Belanger,

The LUX calculation code only works with Si1133. 
As long as the software is used with Silicon Lab's sensor product, 
I don't see any problem.

Regards,
Tony
" 

Signed-off-by: Maxime Roussin-Bélanger <maxime.roussinbelanger@gmail.com>
Reviewed-by: Jean-Francois Dagenais <jeff.dagenais@gmail.com>
---
Changes in v3:
        - The default IR measurement is the "medium ir"
        - Added lux calculation as 3 separate channels, this added 
        - Made the channel 0 for all the other raw measurements
	- Remove unused iio_chan_spec properties
        - Removed sampling frequency, it was not used
        - si1133_data now holds 2 arrays of adcconfig and adcsens for each channel
	- New enum to initialize the lux channels cleanly

 .../ABI/testing/sysfs-bus-iio-light-si1133    |   29 +
 drivers/iio/light/Kconfig                     |   12 +
 drivers/iio/light/Makefile                    |    1 +
 drivers/iio/light/si1133.c                    | 1081 +++++++++++++++++
 4 files changed, 1123 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-light-si1133
 create mode 100644 drivers/iio/light/si1133.c

diff --git a/Documentation/ABI/testing/sysfs-bus-iio-light-si1133 b/Documentation/ABI/testing/sysfs-bus-iio-light-si1133
new file mode 100644
index 000000000000..9c0aa73a0916
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-light-si1133
@@ -0,0 +1,29 @@
+What:		/sys/bus/iio/devices/iio:deviceX/in_intensity_ir_small_raw
+KernelVersion:	4.18
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Unit-less infrared intensity. The intensity is measured from 1
+		dark photodiode. "small" indicate the surface area capturing
+		infrared.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_intensity_ir_large_raw
+KernelVersion:	4.18
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Unit-less infrared intensity. The intensity is measured from 4
+		dark photodiodes. "large" indicate the surface area capturing
+		infrared.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_intensity_large_raw
+KernelVersion:	4.18
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Unit-less light intensity with more diodes.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_uvindex_deep_raw
+KernelVersion:	4.18
+Contact:	linux-iio@vger.kernel.org
+Description:
+		UV light intensity index measuring the human skin's response to
+		deep ultraviolet (DUV) wavelength of sunlight weighted according
+		to the standardised CIE Erythemal Action Spectrum.
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index ba2e64d7ee58..86335cfe0dcf 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -1,3 +1,4 @@
+
 #
 # Light sensors
 #
@@ -267,6 +268,17 @@ config PA12203001
          This driver can also be built as a module.  If so, the module
          will be called pa12203001.
 
+config SI1133
+	tristate "SI1133 UV Index Sensor and Ambient Light Sensor"
+	depends on I2C
+	select REGMAP_I2C
+	  help
+	  Say Y here if you want to build a driver for the Silicon Labs SI1133
+	  UV Index Sensor and Ambient Light Sensor chip.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called si1133.
+
 config SI1145
 	tristate "SI1132 and SI1141/2/3/5/6/7 combined ALS, UV index and proximity sensor"
 	depends on I2C
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index c5768df87a17..955ed83e03f9 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_OPT3001)		+= opt3001.o
 obj-$(CONFIG_PA12203001)	+= pa12203001.o
 obj-$(CONFIG_RPR0521)		+= rpr0521.o
 obj-$(CONFIG_SENSORS_TSL2563)	+= tsl2563.o
+obj-$(CONFIG_SI1133)		+= si1133.o
 obj-$(CONFIG_SI1145)		+= si1145.o
 obj-$(CONFIG_STK3310)          += stk3310.o
 obj-$(CONFIG_TCS3414)		+= tcs3414.o
diff --git a/drivers/iio/light/si1133.c b/drivers/iio/light/si1133.c
new file mode 100644
index 000000000000..af8557b9b34d
--- /dev/null
+++ b/drivers/iio/light/si1133.c
@@ -0,0 +1,1081 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * si1133.c - Support for Silabs SI1133 combined ambient
+ * light and UV index sensors
+ *
+ * Copyright 2018 Maxime Roussin-Belanger <maxime.roussinbelanger@gmail.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include <linux/util_macros.h>
+
+#define SI1133_REG_PART_ID		0x00
+#define SI1133_REG_REV_ID		0x01
+#define SI1133_REG_MFR_ID		0x02
+#define SI1133_REG_INFO0		0x03
+#define SI1133_REG_INFO1		0x04
+
+#define SI1133_PART_ID			0x33
+
+#define SI1133_REG_HOSTIN0		0x0A
+#define SI1133_REG_COMMAND		0x0B
+#define SI1133_REG_IRQ_ENABLE		0x0F
+#define SI1133_REG_RESPONSE1		0x10
+#define SI1133_REG_RESPONSE0		0x11
+#define SI1133_REG_IRQ_STATUS		0x12
+#define SI1133_REG_MEAS_RATE		0x1A
+
+#define SI1133_IRQ_CHANNEL_ENABLE	0xF
+
+#define SI1133_CMD_RESET_CTR		0x00
+#define SI1133_CMD_RESET_SW		0x01
+#define SI1133_CMD_FORCE		0x11
+#define SI1133_CMD_START_AUTONOMOUS	0x13
+#define SI1133_CMD_PARAM_SET		0x80
+#define SI1133_CMD_PARAM_QUERY		0x40
+#define SI1133_CMD_PARAM_MASK		0x3F
+
+#define SI1133_CMD_ERR_MASK		BIT(4)
+#define SI1133_CMD_SEQ_MASK		0xF
+#define SI1133_MAX_CMD_CTR		0xF
+
+#define SI1133_PARAM_REG_CHAN_LIST	0x01
+#define SI1133_PARAM_REG_ADCCONFIG(x)	(((x) * 4) + 2)
+#define SI1133_PARAM_REG_ADCSENS(x)	(((x) * 4) + 3)
+#define SI1133_PARAM_REG_ADCPOST(x)	(((x) * 4) + 4)
+
+#define SI1133_ADCMUX_MASK 0x1F
+
+#define SI1133_ADCCONFIG_DECIM_RATE(x)	((x) << 5)
+
+#define SI1133_ADCSENS_SCALE_MASK 0x70
+#define SI1133_ADCSENS_SCALE_SHIFT 4
+#define SI1133_ADCSENS_HSIG_MASK BIT(7)
+#define SI1133_ADCSENS_HSIG_SHIFT 7
+#define SI1133_ADCSENS_HW_GAIN_MASK 0xF
+#define SI1133_ADCSENS_NB_MEAS(x)	(fls(x) << SI1133_ADCSENS_SCALE_SHIFT)
+
+#define SI1133_ADCPOST_24BIT_EN BIT(6)
+#define SI1133_ADCPOST_POSTSHIFT_BITQTY(x) ((x & GENMASK(2, 0)) << 3)
+
+#define SI1133_PARAM_ADCMUX_SMALL_IR	0x0
+#define SI1133_PARAM_ADCMUX_MED_IR	0x1
+#define SI1133_PARAM_ADCMUX_LARGE_IR	0x2
+#define SI1133_PARAM_ADCMUX_WHITE	0xB
+#define SI1133_PARAM_ADCMUX_LARGE_WHITE	0xD
+#define SI1133_PARAM_ADCMUX_UV		0x18
+#define SI1133_PARAM_ADCMUX_UV_DEEP	0x19
+
+#define SI1133_ERR_INVALID_CMD		0x0
+#define SI1133_ERR_INVALID_LOCATION_CMD 0x1
+#define SI1133_ERR_SATURATION_ADC_OR_OVERFLOW_ACCUMULATION 0x2
+#define SI1133_ERR_OUTPUT_BUFFER_OVERFLOW 0x3
+
+#define SI1133_COMPLETION_TIMEOUT_MS	500
+
+#define SI1133_CMD_MINSLEEP_US_LOW	5000
+#define SI1133_CMD_MINSLEEP_US_HIGH	7500
+#define SI1133_CMD_TIMEOUT_MS		25
+#define SI1133_CMD_LUX_TIMEOUT_MS	5000
+#define SI1133_CMD_TIMEOUT_US		(SI1133_CMD_TIMEOUT_MS * 1000)
+
+#define SI1133_REG_HOSTOUT(x)		((x) + 0x13)
+
+#define SI1133_MEASUREMENT_FREQUENCY 1250
+
+#define X_ORDER_MASK            0x0070
+#define Y_ORDER_MASK            0x0007
+#define SIGN_MASK               0x0080
+#define get_x_order(m)          (((m) & X_ORDER_MASK) >> 4)
+#define get_y_order(m)          (((m) & Y_ORDER_MASK))
+#define get_sign(m)             (((m) & SIGN_MASK) >> 7)
+
+#define SI1133_LUX_ADC_MASK		0xE
+#define SI1133_ADC_THRESHOLD		16000
+#define SI1133_INPUT_FRACTION_HIGH	7
+#define SI1133_INPUT_FRACTION_LOW	15
+#define SI1133_LUX_OUTPUT_FRACTION	12
+#define SI1133_LUX_BUFFER_SIZE		9
+
+static const int si1133_scale_available[] = {
+	1, 2, 4, 8, 16, 32, 64, 128};
+
+static IIO_CONST_ATTR(scale_available, "1 2 4 8 16 32 64 128");
+
+static IIO_CONST_ATTR_INT_TIME_AVAIL("0.0244 0.0488 0.0975 0.195 0.390 0.780 "
+				     "1.560 3.120 6.24 12.48 25.0 50.0");
+
+/* A.K.A. HW_GAIN in datasheet */
+enum si1133_int_time {
+	    _24_4_us = 0,
+	    _48_8_us = 1,
+	    _97_5_us = 2,
+	   _195_0_us = 3,
+	   _390_0_us = 4,
+	   _780_0_us = 5,
+	 _1_560_0_us = 6,
+	 _3_120_0_us = 7,
+	 _6_240_0_us = 8,
+	_12_480_0_us = 9,
+	_25_ms = 10,
+	_50_ms = 11,
+};
+
+/* Integration time in milliseconds, nanoseconds */
+static const int si1133_int_time_table[][2] = {
+	[_24_4_us] = {0, 24400},
+	[_48_8_us] = {0, 48800},
+	[_97_5_us] = {0, 97500},
+	[_195_0_us] = {0, 195000},
+	[_390_0_us] = {0, 390000},
+	[_780_0_us] = {0, 780000},
+	[_1_560_0_us] = {1, 560000},
+	[_3_120_0_us] = {3, 120000},
+	[_6_240_0_us] = {6, 240000},
+	[_12_480_0_us] = {12, 480000},
+	[_25_ms] = {25, 000000},
+	[_50_ms] = {50, 000000},
+};
+
+static const struct regmap_range si1133_reg_ranges[] = {
+	regmap_reg_range(0x00, 0x02),
+	regmap_reg_range(0x0A, 0x0B),
+	regmap_reg_range(0x0F, 0x0F),
+	regmap_reg_range(0x10, 0x12),
+	regmap_reg_range(0x13, 0x2C),
+};
+
+static const struct regmap_range si1133_reg_ro_ranges[] = {
+	regmap_reg_range(0x00, 0x02),
+	regmap_reg_range(0x10, 0x2C),
+};
+
+static const struct regmap_range si1133_precious_ranges[] = {
+	regmap_reg_range(0x12, 0x12),
+};
+
+static const struct regmap_access_table si1133_write_ranges_table = {
+	.yes_ranges	= si1133_reg_ranges,
+	.n_yes_ranges	= ARRAY_SIZE(si1133_reg_ranges),
+	.no_ranges	= si1133_reg_ro_ranges,
+	.n_no_ranges	= ARRAY_SIZE(si1133_reg_ro_ranges),
+};
+
+static const struct regmap_access_table si1133_read_ranges_table = {
+	.yes_ranges	= si1133_reg_ranges,
+	.n_yes_ranges	= ARRAY_SIZE(si1133_reg_ranges),
+};
+
+static const struct regmap_access_table si1133_precious_table = {
+	.yes_ranges	= si1133_precious_ranges,
+	.n_yes_ranges	= ARRAY_SIZE(si1133_precious_ranges),
+};
+
+static const struct regmap_config si1133_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = 0x2C,
+
+	.wr_table = &si1133_write_ranges_table,
+	.rd_table = &si1133_read_ranges_table,
+
+	.precious_table = &si1133_precious_table,
+};
+
+struct si1133_data {
+	struct regmap *regmap;
+	struct i2c_client *client;
+
+	/* Lock protecting one command at a time can be processed */
+	struct mutex mutex;
+
+	int rsp_seq;
+	u8 scan_mask;
+	u8 adc_sens[6];
+	u8 adc_config[6];
+
+	struct completion completion;
+};
+
+struct si1133_coeff {
+	s16 info;
+	u16 mag;
+};
+
+struct si1133_lux_coeff {
+	struct si1133_coeff coeff_high[4];
+	struct si1133_coeff coeff_low[9];
+};
+
+static const struct si1133_lux_coeff lux_coeff = {
+	{
+		{  0,   209},
+		{ 1665,  93},
+		{ 2064,  65},
+		{-2671, 234}
+	},
+	{
+		{    0,     0},
+		{ 1921, 29053},
+		{-1022, 36363},
+		{ 2320, 20789},
+		{ -367, 57909},
+		{-1774, 38240},
+		{ -608, 46775},
+		{-1503, 51831},
+		{-1886, 58928}
+	}
+};
+
+int si1133_calculate_polynomial_inner(u32 input, u8 fraction, u16 mag, s8 shift)
+{
+	return ((input << fraction) / mag) << shift;
+}
+
+int si1133_calculate_output(u32 x, u32 y, u8 x_order, u8 y_order,
+			    u8 input_fraction, s8 sign,
+			    struct si1133_coeff *coeffs)
+{
+	s8 shift;
+	int x1 = 1;
+	int x2 = 1;
+	int y1 = 1;
+	int y2 = 1;
+
+	shift = ((u16)coeffs->info & 0xFF00) >> 8;
+	shift ^= 0xFF;
+	shift += 1;
+	shift = -shift;
+
+	if (x_order > 0) {
+		x1 = si1133_calculate_polynomial_inner(x, input_fraction,
+						       coeffs->mag, shift);
+		if (x_order > 1) {
+			x2 = si1133_calculate_polynomial_inner(x,
+							       input_fraction,
+							       coeffs->mag,
+							       shift);
+		}
+	}
+
+	if (y_order > 0) {
+		y1 = si1133_calculate_polynomial_inner(y, input_fraction,
+						       coeffs->mag, shift);
+		if (y_order > 1) {
+			y2 = si1133_calculate_polynomial_inner(y,
+							       input_fraction,
+							       coeffs->mag,
+							       shift);
+		}
+	}
+
+	return sign * x1 * x2 * y1 * y2;
+}
+
+/*
+ * The algorithm is from:
+ * https://siliconlabs.github.io/Gecko_SDK_Doc/efm32zg/html/si1133_8c_source.html#l00716
+ */
+static int si1133_calc_polynomial(u32 x, u32 y, u8 input_fraction, u8 num_coeff,
+				  struct si1133_coeff *coeffs)
+{
+	u8 x_order, y_order;
+	u8 counter;
+	s8 sign;
+	int output = 0;
+
+	for (counter = 0; counter < num_coeff; counter++) {
+		if (coeffs->info < 0)
+			sign = -1;
+		else
+			sign = 1;
+
+		x_order = get_x_order(coeffs->info);
+		y_order = get_y_order(coeffs->info);
+
+		if ((x_order == 0) && (y_order == 0))
+			output +=
+			       sign * coeffs->mag << SI1133_LUX_OUTPUT_FRACTION;
+		else
+			output += si1133_calculate_output(x, y, x_order,
+							  y_order,
+							  input_fraction, sign,
+							  coeffs);
+		coeffs++;
+	}
+
+	return abs(output);
+}
+
+static int si1133_cmd_reset_sw(struct si1133_data *data)
+{
+	struct device *dev = &data->client->dev;
+	unsigned int resp;
+	unsigned long timeout;
+	int err;
+
+	err = regmap_write(data->regmap, SI1133_REG_COMMAND,
+			   SI1133_CMD_RESET_SW);
+	if (err)
+		return err;
+
+	timeout = jiffies + msecs_to_jiffies(SI1133_CMD_TIMEOUT_MS);
+	while (true) {
+		err = regmap_read(data->regmap, SI1133_REG_RESPONSE0, &resp);
+		if (err == -ENXIO) {
+			usleep_range(SI1133_CMD_MINSLEEP_US_LOW,
+				     SI1133_CMD_MINSLEEP_US_HIGH);
+			continue;
+		}
+
+		if ((resp & SI1133_MAX_CMD_CTR) == SI1133_MAX_CMD_CTR)
+			break;
+
+		if (time_after(jiffies, timeout)) {
+			dev_warn(dev, "Timeout on reset ctr resp: %d\n", resp);
+			return -ETIMEDOUT;
+		}
+	}
+
+	if (!err)
+		data->rsp_seq = SI1133_MAX_CMD_CTR;
+
+	return err;
+}
+
+static int si1133_parse_response_err(struct device *dev, u32 resp, u8 cmd)
+{
+	resp &= 0xF;
+
+	switch (resp) {
+	case SI1133_ERR_OUTPUT_BUFFER_OVERFLOW:
+		dev_warn(dev, "Output buffer overflow: %#02hhx\n", cmd);
+		return -EOVERFLOW;
+	case SI1133_ERR_SATURATION_ADC_OR_OVERFLOW_ACCUMULATION:
+		dev_warn(dev, "Saturation of the ADC or overflow of accumulation: %#02hhx\n",
+			 cmd);
+		return -EOVERFLOW;
+	case SI1133_ERR_INVALID_LOCATION_CMD:
+		dev_warn(dev,
+			 "Parameter access to an invalid location: %#02hhx\n",
+			 cmd);
+		return -EINVAL;
+	case SI1133_ERR_INVALID_CMD:
+		dev_warn(dev, "Invalid command %#02hhx\n", cmd);
+		return -EINVAL;
+	default:
+		dev_warn(dev, "Unknown error %#02hhx\n", cmd);
+		return -EINVAL;
+	}
+}
+
+static int si1133_cmd_reset_counter(struct si1133_data *data)
+{
+	int err = regmap_write(data->regmap, SI1133_REG_COMMAND,
+			       SI1133_CMD_RESET_CTR);
+	if (err)
+		return err;
+
+	data->rsp_seq = 0;
+
+	return 0;
+}
+
+static int si1133_command(struct si1133_data *data, u8 cmd)
+{
+	struct device *dev = &data->client->dev;
+	u32 resp;
+	int err;
+	int expected_seq;
+
+	mutex_lock(&data->mutex);
+
+	expected_seq = (data->rsp_seq + 1) & SI1133_MAX_CMD_CTR;
+
+	if (cmd == SI1133_CMD_FORCE)
+		reinit_completion(&data->completion);
+
+	err = regmap_write(data->regmap, SI1133_REG_COMMAND, cmd);
+	if (err) {
+		dev_warn(dev, "Failed to write command %#02hhx, ret=%d\n", cmd,
+			 err);
+		goto out;
+	}
+
+	if (cmd == SI1133_CMD_FORCE) {
+		/* wait for irq */
+		if (!wait_for_completion_timeout(&data->completion,
+			msecs_to_jiffies(SI1133_COMPLETION_TIMEOUT_MS))) {
+			err = -ETIMEDOUT;
+			goto out;
+		}
+	} else {
+		err = regmap_read_poll_timeout(data->regmap,
+					       SI1133_REG_RESPONSE0, resp,
+					       (resp & SI1133_CMD_SEQ_MASK) ==
+					       expected_seq ||
+					       (resp & SI1133_CMD_ERR_MASK),
+					       SI1133_CMD_MINSLEEP_US_LOW,
+					       SI1133_CMD_TIMEOUT_MS * 1000);
+		if (err) {
+			dev_warn(dev,
+				 "Failed to read command %#02hhx, ret=%d\n",
+				 cmd, err);
+			goto out;
+		}
+	}
+
+	if (resp & SI1133_CMD_ERR_MASK) {
+		err = si1133_parse_response_err(dev, resp, cmd);
+		si1133_cmd_reset_counter(data);
+	} else {
+		data->rsp_seq = expected_seq;
+	}
+
+out:
+	mutex_unlock(&data->mutex);
+
+	return err;
+}
+
+static int si1133_param_set(struct si1133_data *data, u8 param, u32 value)
+{
+	int err = regmap_write(data->regmap, SI1133_REG_HOSTIN0, value);
+
+	if (err)
+		return err;
+
+	return si1133_command(data, SI1133_CMD_PARAM_SET |
+			      (param & SI1133_CMD_PARAM_MASK));
+}
+
+static int si1133_param_query(struct si1133_data *data, u8 param, u32 *result)
+{
+	int err = si1133_command(data, SI1133_CMD_PARAM_QUERY |
+				 (param & SI1133_CMD_PARAM_MASK));
+	if (err)
+		return err;
+
+	return regmap_read(data->regmap, SI1133_REG_RESPONSE1, result);
+}
+
+#define SI1133_CHANNEL(_ch, _type) \
+	.type = _type, \
+	.channel = _ch, \
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | \
+		BIT(IIO_CHAN_INFO_SCALE) | \
+		BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
+
+static const struct iio_chan_spec si1133_channels[] = {
+	{
+		.type = IIO_LIGHT,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+		.channel = 0,
+	},
+	{
+		SI1133_CHANNEL(SI1133_PARAM_ADCMUX_WHITE, IIO_INTENSITY)
+		.channel2 = IIO_MOD_LIGHT_BOTH,
+	},
+	{
+		SI1133_CHANNEL(SI1133_PARAM_ADCMUX_LARGE_WHITE, IIO_INTENSITY)
+		.channel2 = IIO_MOD_LIGHT_BOTH,
+		.extend_name = "large",
+	},
+	{
+		SI1133_CHANNEL(SI1133_PARAM_ADCMUX_SMALL_IR, IIO_INTENSITY)
+		.extend_name = "small",
+		.modified = 1,
+		.channel2 = IIO_MOD_LIGHT_IR,
+	},
+	{
+		SI1133_CHANNEL(SI1133_PARAM_ADCMUX_MED_IR, IIO_INTENSITY)
+		.modified = 1,
+		.channel2 = IIO_MOD_LIGHT_IR,
+	},
+	{
+		SI1133_CHANNEL(SI1133_PARAM_ADCMUX_LARGE_IR, IIO_INTENSITY)
+		.extend_name = "large",
+		.modified = 1,
+		.channel2 = IIO_MOD_LIGHT_IR,
+	},
+	{
+		SI1133_CHANNEL(SI1133_PARAM_ADCMUX_UV, IIO_UVINDEX)
+	},
+	{
+		SI1133_CHANNEL(SI1133_PARAM_ADCMUX_UV_DEEP, IIO_UVINDEX)
+		.modified = 1,
+		.channel2 = IIO_MOD_LIGHT_DUV,
+	}
+};
+
+static int si1133_get_int_time_index(int milliseconds, int nanoseconds)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(si1133_int_time_table); i++) {
+		if (milliseconds == si1133_int_time_table[i][0] &&
+		    nanoseconds == si1133_int_time_table[i][1])
+			return i;
+	}
+	return -EINVAL;
+}
+
+static int si1133_set_integration_time(struct si1133_data *data, u8 adc,
+				       int milliseconds, int nanoseconds)
+{
+	int index;
+
+	index = si1133_get_int_time_index(milliseconds, nanoseconds);
+	if (index < 0)
+		return index;
+
+	data->adc_sens[adc] &= 0xF0;
+	data->adc_sens[adc] |= index;
+
+	return si1133_param_set(data, SI1133_PARAM_REG_ADCSENS(0),
+				data->adc_sens[adc]);
+}
+
+static int si1133_set_chlist(struct si1133_data *data, u8 scan_mask)
+{
+	/* channel list already set, no need to reprogram */
+	if (data->scan_mask == scan_mask)
+		return 0;
+
+	data->scan_mask = scan_mask;
+
+	return si1133_param_set(data, SI1133_PARAM_REG_CHAN_LIST, scan_mask);
+}
+
+static int si1133_chan_set_adcconfig(struct si1133_data *data, u8 adc,
+				     u8 adc_config)
+{
+	int err;
+
+	err = si1133_param_set(data, SI1133_PARAM_REG_ADCCONFIG(adc),
+			       adc_config);
+	if (err)
+		return err;
+
+	data->adc_config[adc] = adc_config;
+
+	return 0;
+}
+
+static int si1133_update_adcconfig(struct si1133_data *data, uint8_t adc,
+				   u8 mask, u8 shift, u8 value)
+{
+	u32 adc_config;
+	int err;
+
+	err = si1133_param_query(data, SI1133_PARAM_REG_ADCCONFIG(adc),
+				 &adc_config);
+	if (err)
+		return err;
+
+	adc_config &= ~mask;
+	adc_config |= (value << shift);
+
+	return si1133_chan_set_adcconfig(data, adc, adc_config);
+}
+
+static int si1133_set_adcmux(struct si1133_data *data, u8 adc, u8 mux)
+{
+	if ((mux & data->adc_config[adc]) == mux)
+		return 0; /* mux already set to correct value */
+
+	return si1133_update_adcconfig(data, adc, SI1133_ADCMUX_MASK, 0, mux);
+}
+
+static int si1133_force_measurement(struct si1133_data *data)
+{
+	return si1133_command(data, SI1133_CMD_FORCE);
+}
+
+static int si1133_bulk_read(struct si1133_data *data, u8 start_reg, u8 length,
+			    u8 *buffer)
+{
+	int err;
+
+	err = si1133_force_measurement(data);
+	if (err)
+		return err;
+
+	return regmap_bulk_read(data->regmap, start_reg, buffer, length);
+}
+
+static int si1133_measure(struct si1133_data *data,
+			  struct iio_chan_spec const *chan,
+			  int *val)
+{
+	int err;
+
+	__be16 resp;
+
+	err = si1133_set_adcmux(data, 0, chan->channel);
+	if (err)
+		return err;
+
+	/* Deactivate lux measurements if they were active */
+	err = si1133_set_chlist(data, BIT(0));
+	if (err)
+		return err;
+
+	err = si1133_bulk_read(data, SI1133_REG_HOSTOUT(0), sizeof(resp),
+			       (u8 *)&resp);
+	if (err)
+		return err;
+
+	*val = be16_to_cpu(resp);
+
+	return err;
+}
+
+static irqreturn_t si1133_threaded_irq_handler(int irq, void *private)
+{
+	struct iio_dev *iio_dev = private;
+	struct si1133_data *data = iio_priv(iio_dev);
+	u32 irq_status;
+	int err;
+
+	err = regmap_read(data->regmap, SI1133_REG_IRQ_STATUS, &irq_status);
+	if (err) {
+		dev_err_ratelimited(&iio_dev->dev, "Error reading IRQ\n");
+		goto out;
+	}
+
+	if (irq_status != data->scan_mask)
+		return IRQ_NONE;
+
+out:
+	complete(&data->completion);
+
+	return IRQ_HANDLED;
+}
+
+static int si1133_scale_to_swgain(int scale_integer, int scale_fractional)
+{
+	scale_integer = find_closest(scale_integer, si1133_scale_available,
+				     ARRAY_SIZE(si1133_scale_available));
+	if (scale_integer < 0 ||
+	    scale_integer > ARRAY_SIZE(si1133_scale_available) ||
+	    scale_fractional != 0)
+		return -EINVAL;
+
+	return scale_integer;
+}
+
+static int si1133_chan_set_adcsens(struct si1133_data *data, u8 adc,
+				   u8 adc_sens)
+{
+	int err;
+
+	err = si1133_param_set(data, SI1133_PARAM_REG_ADCSENS(adc), adc_sens);
+	if (err)
+		return err;
+
+	data->adc_sens[adc] = adc_sens;
+
+	return 0;
+}
+
+static int si1133_update_adcsens(struct si1133_data *data, u8 mask,
+				 u8 shift, u8 value)
+{
+	int err;
+	u32 adc_sens;
+
+	err = si1133_param_query(data, SI1133_PARAM_REG_ADCSENS(0),
+				 &adc_sens);
+	if (err)
+		return err;
+
+	adc_sens &= ~mask;
+	adc_sens |= (value << shift);
+
+	return si1133_chan_set_adcsens(data, 0, adc_sens);
+}
+
+static int si1133_get_lux(struct si1133_data *data, int *val)
+{
+	int err;
+	int lux;
+	u32 high_vis;
+	u32 low_vis;
+	u32 ir;
+	u8 buffer[SI1133_LUX_BUFFER_SIZE];
+
+	/* Activate lux channels */
+	err = si1133_set_chlist(data, SI1133_LUX_ADC_MASK);
+	if (err)
+		return err;
+
+	err = si1133_bulk_read(data, SI1133_REG_HOSTOUT(0),
+			       SI1133_LUX_BUFFER_SIZE, buffer);
+	if (err)
+		return err;
+
+	high_vis = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2];
+	low_vis = (buffer[3] << 16) | (buffer[4] << 8) | buffer[5];
+	ir = (buffer[6] << 16) | (buffer[7] << 8) | buffer[8];
+
+	if (high_vis > SI1133_ADC_THRESHOLD || ir > SI1133_ADC_THRESHOLD)
+		lux = si1133_calc_polynomial(high_vis, ir,
+					     SI1133_INPUT_FRACTION_HIGH,
+					     ARRAY_SIZE(lux_coeff.coeff_high),
+					     &lux_coeff.coeff_high[0]);
+	else
+		lux = si1133_calc_polynomial(low_vis, ir,
+					     SI1133_INPUT_FRACTION_LOW,
+					     ARRAY_SIZE(lux_coeff.coeff_low),
+					     &lux_coeff.coeff_low[0]);
+
+	*val = lux >> SI1133_LUX_OUTPUT_FRACTION;
+
+	return err;
+}
+
+static int si1133_read_raw(struct iio_dev *iio_dev,
+			   struct iio_chan_spec const *chan,
+			   int *val, int *val2, long mask)
+{
+	struct si1133_data *data = iio_priv(iio_dev);
+	u8 adc_sens = data->adc_sens[0];
+	int err;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_PROCESSED:
+		switch (chan->type) {
+		case IIO_LIGHT:
+			err = si1133_get_lux(data, val);
+			if (err)
+				return err;
+
+			return IIO_VAL_INT;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_RAW:
+		switch (chan->type) {
+		case IIO_INTENSITY:
+		case IIO_UVINDEX:
+			err = si1133_measure(data, chan, val);
+			if (err)
+				return err;
+
+			return IIO_VAL_INT;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_INT_TIME:
+		switch (chan->type) {
+		case IIO_INTENSITY:
+		case IIO_UVINDEX:
+			adc_sens &= SI1133_ADCSENS_HW_GAIN_MASK;
+
+			*val = si1133_int_time_table[adc_sens][0];
+			*val2 = si1133_int_time_table[adc_sens][1];
+			return IIO_VAL_INT_PLUS_MICRO;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_INTENSITY:
+		case IIO_UVINDEX:
+			adc_sens &= SI1133_ADCSENS_SCALE_MASK;
+			adc_sens >>= SI1133_ADCSENS_SCALE_SHIFT;
+
+			*val = BIT(adc_sens);
+
+			return IIO_VAL_INT;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_HARDWAREGAIN:
+		switch (chan->type) {
+		case IIO_INTENSITY:
+		case IIO_UVINDEX:
+			adc_sens >>= SI1133_ADCSENS_HSIG_SHIFT;
+
+			*val = adc_sens;
+
+			return IIO_VAL_INT;
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int si1133_write_raw(struct iio_dev *iio_dev,
+			    struct iio_chan_spec const *chan,
+			    int val, int val2, long mask)
+{
+	struct si1133_data *data = iio_priv(iio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_INTENSITY:
+		case IIO_UVINDEX:
+			val = si1133_scale_to_swgain(val, val2);
+			if (val < 0)
+				return val;
+
+			return si1133_update_adcsens(data,
+						     SI1133_ADCSENS_SCALE_MASK,
+						     SI1133_ADCSENS_SCALE_SHIFT,
+						     val);
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_INT_TIME:
+		return si1133_set_integration_time(data, 0, val, val2);
+	case IIO_CHAN_INFO_HARDWAREGAIN:
+		switch (chan->type) {
+		case IIO_INTENSITY:
+		case IIO_UVINDEX:
+			if (val != 0 || val != 1)
+				return -EINVAL;
+
+			return si1133_update_adcsens(data,
+						     SI1133_ADCSENS_HSIG_MASK,
+						     SI1133_ADCSENS_HSIG_SHIFT,
+						     val);
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static struct attribute *si1133_attributes[] = {
+	&iio_const_attr_integration_time_available.dev_attr.attr,
+	&iio_const_attr_scale_available.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group si1133_attribute_group = {
+	.attrs = si1133_attributes,
+};
+
+static const struct iio_info si1133_info = {
+	.read_raw = si1133_read_raw,
+	.write_raw = si1133_write_raw,
+	.attrs = &si1133_attribute_group,
+};
+
+/*
+ * si1133_init_lux_channels - Configure 3 different channels(adc) (1,2 and 3)
+ * The channel configuration for the lux measurement was taken from :
+ * https://siliconlabs.github.io/Gecko_SDK_Doc/efm32zg/html/si1133_8c_source.html#l00578
+ *
+ * Reserved the channel 0 for the other raw measurements
+ */
+static int si1133_init_lux_channels(struct si1133_data *data)
+{
+	int err;
+
+	err = si1133_chan_set_adcconfig(data, 1,
+					SI1133_ADCCONFIG_DECIM_RATE(1) |
+					SI1133_PARAM_ADCMUX_LARGE_WHITE);
+	if (err)
+		return err;
+
+	err = si1133_param_set(data, SI1133_PARAM_REG_ADCPOST(1),
+			       SI1133_ADCPOST_24BIT_EN |
+			       SI1133_ADCPOST_POSTSHIFT_BITQTY(0));
+	if (err)
+		return err;
+	err = si1133_chan_set_adcsens(data, 1, SI1133_ADCSENS_HSIG_MASK |
+				      SI1133_ADCSENS_NB_MEAS(64) | _48_8_us);
+	if (err)
+		return err;
+
+	err = si1133_chan_set_adcconfig(data, 2,
+					SI1133_ADCCONFIG_DECIM_RATE(1) |
+					SI1133_PARAM_ADCMUX_LARGE_WHITE);
+	if (err)
+		return err;
+
+	err = si1133_param_set(data, SI1133_PARAM_REG_ADCPOST(2),
+			       SI1133_ADCPOST_24BIT_EN |
+			       SI1133_ADCPOST_POSTSHIFT_BITQTY(2));
+	if (err)
+		return err;
+
+	err = si1133_chan_set_adcsens(data, 2, SI1133_ADCSENS_HSIG_MASK |
+				      SI1133_ADCSENS_NB_MEAS(1) | _3_120_0_us);
+	if (err)
+		return err;
+
+	err = si1133_chan_set_adcconfig(data, 3,
+					SI1133_ADCCONFIG_DECIM_RATE(1) |
+					SI1133_PARAM_ADCMUX_MED_IR);
+	if (err)
+		return err;
+
+	err = si1133_param_set(data, SI1133_PARAM_REG_ADCPOST(3),
+			       SI1133_ADCPOST_24BIT_EN |
+			       SI1133_ADCPOST_POSTSHIFT_BITQTY(2));
+	if (err)
+		return err;
+
+	err = si1133_chan_set_adcsens(data, 3, SI1133_ADCSENS_HSIG_MASK |
+				      SI1133_ADCSENS_NB_MEAS(64) | _48_8_us);
+	if (err)
+		return err;
+
+	return err;
+}
+
+static int si1133_initialize(struct si1133_data *data)
+{
+	int err;
+
+	err = si1133_cmd_reset_sw(data);
+	if (err)
+		return err;
+
+	/* Turn off autonomous mode */
+	err = si1133_param_set(data, SI1133_REG_MEAS_RATE, 0);
+	if (err)
+		return err;
+
+	err = si1133_init_lux_channels(data);
+	if (err)
+		return err;
+
+	return regmap_write(data->regmap, SI1133_REG_IRQ_ENABLE,
+			    SI1133_IRQ_CHANNEL_ENABLE);
+}
+
+static int si1133_validate_ids(struct iio_dev *iio_dev)
+{
+	struct si1133_data *data = iio_priv(iio_dev);
+
+	unsigned int part_id, rev_id, mfr_id;
+	int err;
+
+	err = regmap_read(data->regmap, SI1133_REG_PART_ID, &part_id);
+	if (err)
+		return err;
+
+	err = regmap_read(data->regmap, SI1133_REG_REV_ID, &rev_id);
+	if (err)
+		return err;
+
+	err = regmap_read(data->regmap, SI1133_REG_MFR_ID, &mfr_id);
+	if (err)
+		return err;
+
+	dev_info(&iio_dev->dev,
+		 "Device ID part %#02hhx rev %#02hhx mfr %#02hhx\n",
+		 part_id, rev_id, mfr_id);
+	if (part_id != SI1133_PART_ID) {
+		dev_err(&iio_dev->dev,
+			"Part ID mismatch got %#02hhx, expected %#02x\n",
+			part_id, SI1133_PART_ID);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int si1133_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct si1133_data *data;
+	struct iio_dev *iio_dev;
+	int err;
+
+	iio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+	if (!iio_dev)
+		return -ENOMEM;
+
+	data = iio_priv(iio_dev);
+
+	init_completion(&data->completion);
+
+	data->regmap = devm_regmap_init_i2c(client, &si1133_regmap_config);
+	if (IS_ERR(data->regmap)) {
+		err = PTR_ERR(data->regmap);
+		dev_err(&client->dev, "Failed to initialise regmap: %d\n", err);
+		return err;
+	}
+
+	i2c_set_clientdata(client, iio_dev);
+	data->client = client;
+
+	iio_dev->dev.parent = &client->dev;
+	iio_dev->name = id->name;
+	iio_dev->channels = si1133_channels;
+	iio_dev->num_channels = ARRAY_SIZE(si1133_channels);
+	iio_dev->info = &si1133_info;
+	iio_dev->modes = INDIO_DIRECT_MODE;
+
+	mutex_init(&data->mutex);
+
+	err = si1133_validate_ids(iio_dev);
+	if (err)
+		return err;
+
+	err = si1133_initialize(data);
+	if (err) {
+		dev_err(&client->dev,
+			"Error when initializing chip: %d\n", err);
+		return err;
+	}
+
+	if (!client->irq) {
+		dev_err(&client->dev,
+			"Required interrupt not provided, cannot proceed\n");
+		return -EINVAL;
+	}
+
+	err = devm_request_threaded_irq(&client->dev, client->irq,
+					NULL,
+					si1133_threaded_irq_handler,
+					IRQF_ONESHOT | IRQF_SHARED,
+					client->name, iio_dev);
+	if (err) {
+		dev_warn(&client->dev, "Request irq %d failed: %i\n",
+			 client->irq, err);
+		return err;
+	}
+
+	return devm_iio_device_register(&client->dev, iio_dev);
+}
+
+static const struct i2c_device_id si1133_ids[] = {
+	{ "si1133", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, si1133_ids);
+
+static struct i2c_driver si1133_driver = {
+	.driver = {
+	    .name   = "si1133",
+	},
+	.probe  = si1133_probe,
+	.id_table = si1133_ids,
+};
+
+module_i2c_driver(si1133_driver);
+
+MODULE_AUTHOR("Maxime Roussin-Belanger <maxime.roussinbelanger@gmail.com>");
+MODULE_DESCRIPTION("Silabs SI1133, UV index sensor and ambient light sensor driver");
+MODULE_LICENSE("GPL");
-- 
2.18.0


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

* Re: [PATCH v3 2/2] iio: light: introduce si1133
  2018-07-19 20:26 ` [PATCH v3 2/2] iio: light: introduce si1133 Maxime Roussin-Bélanger
@ 2018-07-21 16:06   ` Jonathan Cameron
  0 siblings, 0 replies; 4+ messages in thread
From: Jonathan Cameron @ 2018-07-21 16:06 UTC (permalink / raw
  To: Maxime Roussin-Bélanger
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	linux-iio, Jean-Francois Dagenais, gregkh

On Thu, 19 Jul 2018 16:26:25 -0400
Maxime Roussin-B=C3=A9langer         <maxime.roussinbelanger@gmail.com> wro=
te:

> e-mail received from Silicon Lab to confirm that the licensing
> isn't a problem.
>=20
> "
> Dear Maxime Roussin-Belanger,
>=20
> The LUX calculation code only works with Si1133.=20
> As long as the software is used with Silicon Lab's sensor product,=20
> I don't see any problem.
>=20
> Regards,
> Tony
> "

Would have been nice if they had been a little more descriptive, but
I think that is reasonable grounds to release it under the GPL.

If anyone else disagrees, please shout quickly!  I've cc'd Greg
so he is aware of this when I send a pull request.

Oh for clear language.

>=20
> Signed-off-by: Maxime Roussin-B=C3=A9langer <maxime.roussinbelanger@gmail=
.com>
> Reviewed-by: Jean-Francois Dagenais <jeff.dagenais@gmail.com>

Hmm.  There are a few minor things in here that I think need tidying up.

I've done it and applied, but please check my changes and get back to me
if I've messed anything up!  Particularly around the polynomial
stuff where the original seems to be bonkers in terms of efficiency.
(though I would imagine the compiler will notice and optimize
it out).

Pushed out as testing for the autobuilders to play with it.

Thanks,

Jonathan

> ---
> Changes in v3:
>         - The default IR measurement is the "medium ir"
>         - Added lux calculation as 3 separate channels, this added=20
>         - Made the channel 0 for all the other raw measurements
> 	- Remove unused iio_chan_spec properties
>         - Removed sampling frequency, it was not used
>         - si1133_data now holds 2 arrays of adcconfig and adcsens for eac=
h channel
> 	- New enum to initialize the lux channels cleanly
>=20
>  .../ABI/testing/sysfs-bus-iio-light-si1133    |   29 +
>  drivers/iio/light/Kconfig                     |   12 +
>  drivers/iio/light/Makefile                    |    1 +
>  drivers/iio/light/si1133.c                    | 1081 +++++++++++++++++
>  4 files changed, 1123 insertions(+)
>  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-light-si1133
>  create mode 100644 drivers/iio/light/si1133.c
>=20
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-light-si1133 b/Docum=
entation/ABI/testing/sysfs-bus-iio-light-si1133
> new file mode 100644
> index 000000000000..9c0aa73a0916
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-light-si1133
> @@ -0,0 +1,29 @@
> +What:		/sys/bus/iio/devices/iio:deviceX/in_intensity_ir_small_raw
> +KernelVersion:	4.18
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Unit-less infrared intensity. The intensity is measured from 1
> +		dark photodiode. "small" indicate the surface area capturing
> +		infrared.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/in_intensity_ir_large_raw
> +KernelVersion:	4.18
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Unit-less infrared intensity. The intensity is measured from 4
> +		dark photodiodes. "large" indicate the surface area capturing
> +		infrared.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/in_intensity_large_raw
> +KernelVersion:	4.18
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Unit-less light intensity with more diodes.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/in_uvindex_deep_raw
> +KernelVersion:	4.18
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		UV light intensity index measuring the human skin's response to
> +		deep ultraviolet (DUV) wavelength of sunlight weighted according
> +		to the standardised CIE Erythemal Action Spectrum.

You got rid of this last one with introducing the new modifier.

> diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
> index ba2e64d7ee58..86335cfe0dcf 100644
> --- a/drivers/iio/light/Kconfig
> +++ b/drivers/iio/light/Kconfig
> @@ -1,3 +1,4 @@
> +
>  #
>  # Light sensors
>  #
> @@ -267,6 +268,17 @@ config PA12203001
>           This driver can also be built as a module.  If so, the module
>           will be called pa12203001.
> =20
> +config SI1133
> +	tristate "SI1133 UV Index Sensor and Ambient Light Sensor"
> +	depends on I2C
> +	select REGMAP_I2C
> +	  help
> +	  Say Y here if you want to build a driver for the Silicon Labs SI1133
> +	  UV Index Sensor and Ambient Light Sensor chip.
> +
> +	  To compile this driver as a module, choose M here: the module will be
> +	  called si1133.
> +
>  config SI1145
>  	tristate "SI1132 and SI1141/2/3/5/6/7 combined ALS, UV index and proxim=
ity sensor"
>  	depends on I2C
> diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
> index c5768df87a17..955ed83e03f9 100644
> --- a/drivers/iio/light/Makefile
> +++ b/drivers/iio/light/Makefile
> @@ -26,6 +26,7 @@ obj-$(CONFIG_OPT3001)		+=3D opt3001.o
>  obj-$(CONFIG_PA12203001)	+=3D pa12203001.o
>  obj-$(CONFIG_RPR0521)		+=3D rpr0521.o
>  obj-$(CONFIG_SENSORS_TSL2563)	+=3D tsl2563.o
> +obj-$(CONFIG_SI1133)		+=3D si1133.o
>  obj-$(CONFIG_SI1145)		+=3D si1145.o
>  obj-$(CONFIG_STK3310)          +=3D stk3310.o
>  obj-$(CONFIG_TCS3414)		+=3D tcs3414.o
> diff --git a/drivers/iio/light/si1133.c b/drivers/iio/light/si1133.c
> new file mode 100644
> index 000000000000..af8557b9b34d
> --- /dev/null
> +++ b/drivers/iio/light/si1133.c
> @@ -0,0 +1,1081 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * si1133.c - Support for Silabs SI1133 combined ambient
> + * light and UV index sensors
> + *
> + * Copyright 2018 Maxime Roussin-Belanger <maxime.roussinbelanger@gmail.=
com>
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +
> +#include <linux/util_macros.h>
> +
> +#define SI1133_REG_PART_ID		0x00
> +#define SI1133_REG_REV_ID		0x01
> +#define SI1133_REG_MFR_ID		0x02
> +#define SI1133_REG_INFO0		0x03
> +#define SI1133_REG_INFO1		0x04
> +
> +#define SI1133_PART_ID			0x33
> +
> +#define SI1133_REG_HOSTIN0		0x0A
> +#define SI1133_REG_COMMAND		0x0B
> +#define SI1133_REG_IRQ_ENABLE		0x0F
> +#define SI1133_REG_RESPONSE1		0x10
> +#define SI1133_REG_RESPONSE0		0x11
> +#define SI1133_REG_IRQ_STATUS		0x12
> +#define SI1133_REG_MEAS_RATE		0x1A
> +
> +#define SI1133_IRQ_CHANNEL_ENABLE	0xF
> +
> +#define SI1133_CMD_RESET_CTR		0x00
> +#define SI1133_CMD_RESET_SW		0x01
> +#define SI1133_CMD_FORCE		0x11
> +#define SI1133_CMD_START_AUTONOMOUS	0x13
> +#define SI1133_CMD_PARAM_SET		0x80
> +#define SI1133_CMD_PARAM_QUERY		0x40
> +#define SI1133_CMD_PARAM_MASK		0x3F
> +
> +#define SI1133_CMD_ERR_MASK		BIT(4)
> +#define SI1133_CMD_SEQ_MASK		0xF
> +#define SI1133_MAX_CMD_CTR		0xF
> +
> +#define SI1133_PARAM_REG_CHAN_LIST	0x01
> +#define SI1133_PARAM_REG_ADCCONFIG(x)	(((x) * 4) + 2)
> +#define SI1133_PARAM_REG_ADCSENS(x)	(((x) * 4) + 3)
> +#define SI1133_PARAM_REG_ADCPOST(x)	(((x) * 4) + 4)
> +
> +#define SI1133_ADCMUX_MASK 0x1F
> +
> +#define SI1133_ADCCONFIG_DECIM_RATE(x)	((x) << 5)
> +
> +#define SI1133_ADCSENS_SCALE_MASK 0x70
> +#define SI1133_ADCSENS_SCALE_SHIFT 4
> +#define SI1133_ADCSENS_HSIG_MASK BIT(7)
> +#define SI1133_ADCSENS_HSIG_SHIFT 7
> +#define SI1133_ADCSENS_HW_GAIN_MASK 0xF
> +#define SI1133_ADCSENS_NB_MEAS(x)	(fls(x) << SI1133_ADCSENS_SCALE_SHIFT)
> +
> +#define SI1133_ADCPOST_24BIT_EN BIT(6)
> +#define SI1133_ADCPOST_POSTSHIFT_BITQTY(x) ((x & GENMASK(2, 0)) << 3)
> +
> +#define SI1133_PARAM_ADCMUX_SMALL_IR	0x0
> +#define SI1133_PARAM_ADCMUX_MED_IR	0x1
> +#define SI1133_PARAM_ADCMUX_LARGE_IR	0x2
> +#define SI1133_PARAM_ADCMUX_WHITE	0xB
> +#define SI1133_PARAM_ADCMUX_LARGE_WHITE	0xD
> +#define SI1133_PARAM_ADCMUX_UV		0x18
> +#define SI1133_PARAM_ADCMUX_UV_DEEP	0x19
> +
> +#define SI1133_ERR_INVALID_CMD		0x0
> +#define SI1133_ERR_INVALID_LOCATION_CMD 0x1
> +#define SI1133_ERR_SATURATION_ADC_OR_OVERFLOW_ACCUMULATION 0x2
> +#define SI1133_ERR_OUTPUT_BUFFER_OVERFLOW 0x3
> +
> +#define SI1133_COMPLETION_TIMEOUT_MS	500
> +
> +#define SI1133_CMD_MINSLEEP_US_LOW	5000
> +#define SI1133_CMD_MINSLEEP_US_HIGH	7500
> +#define SI1133_CMD_TIMEOUT_MS		25
> +#define SI1133_CMD_LUX_TIMEOUT_MS	5000
> +#define SI1133_CMD_TIMEOUT_US		(SI1133_CMD_TIMEOUT_MS * 1000)
> +
> +#define SI1133_REG_HOSTOUT(x)		((x) + 0x13)
As below, not seeing why you want brackets here.
> +
> +#define SI1133_MEASUREMENT_FREQUENCY 1250
> +
> +#define X_ORDER_MASK            0x0070
This all need prefixes.
> +#define Y_ORDER_MASK            0x0007
> +#define SIGN_MASK               0x0080
> +#define get_x_order(m)          (((m) & X_ORDER_MASK) >> 4)

I can't see why you'd ever need the outer brackets...

> +#define get_y_order(m)          (((m) & Y_ORDER_MASK))
> +#define get_sign(m)             (((m) & SIGN_MASK) >> 7)

This last one isn't used, so I dropped it and the SIGN_MASK define.

> +
> +#define SI1133_LUX_ADC_MASK		0xE
> +#define SI1133_ADC_THRESHOLD		16000
> +#define SI1133_INPUT_FRACTION_HIGH	7
> +#define SI1133_INPUT_FRACTION_LOW	15
> +#define SI1133_LUX_OUTPUT_FRACTION	12
> +#define SI1133_LUX_BUFFER_SIZE		9
> +
> +static const int si1133_scale_available[] =3D {
> +	1, 2, 4, 8, 16, 32, 64, 128};
> +
> +static IIO_CONST_ATTR(scale_available, "1 2 4 8 16 32 64 128");
> +
> +static IIO_CONST_ATTR_INT_TIME_AVAIL("0.0244 0.0488 0.0975 0.195 0.390 0=
.780 "
> +				     "1.560 3.120 6.24 12.48 25.0 50.0");
> +
> +/* A.K.A. HW_GAIN in datasheet */
> +enum si1133_int_time {
> +	    _24_4_us =3D 0,
> +	    _48_8_us =3D 1,
> +	    _97_5_us =3D 2,
> +	   _195_0_us =3D 3,
> +	   _390_0_us =3D 4,
> +	   _780_0_us =3D 5,
> +	 _1_560_0_us =3D 6,
> +	 _3_120_0_us =3D 7,
> +	 _6_240_0_us =3D 8,
> +	_12_480_0_us =3D 9,
> +	_25_ms =3D 10,
> +	_50_ms =3D 11,
> +};
> +
> +/* Integration time in milliseconds, nanoseconds */
> +static const int si1133_int_time_table[][2] =3D {
> +	[_24_4_us] =3D {0, 24400},
> +	[_48_8_us] =3D {0, 48800},
> +	[_97_5_us] =3D {0, 97500},
> +	[_195_0_us] =3D {0, 195000},
> +	[_390_0_us] =3D {0, 390000},
> +	[_780_0_us] =3D {0, 780000},
> +	[_1_560_0_us] =3D {1, 560000},
> +	[_3_120_0_us] =3D {3, 120000},
> +	[_6_240_0_us] =3D {6, 240000},
> +	[_12_480_0_us] =3D {12, 480000},
> +	[_25_ms] =3D {25, 000000},
> +	[_50_ms] =3D {50, 000000},
> +};
> +
> +static const struct regmap_range si1133_reg_ranges[] =3D {
> +	regmap_reg_range(0x00, 0x02),
> +	regmap_reg_range(0x0A, 0x0B),
> +	regmap_reg_range(0x0F, 0x0F),
> +	regmap_reg_range(0x10, 0x12),
> +	regmap_reg_range(0x13, 0x2C),
> +};
> +
> +static const struct regmap_range si1133_reg_ro_ranges[] =3D {
> +	regmap_reg_range(0x00, 0x02),
> +	regmap_reg_range(0x10, 0x2C),
> +};
> +
> +static const struct regmap_range si1133_precious_ranges[] =3D {
> +	regmap_reg_range(0x12, 0x12),
> +};
> +
> +static const struct regmap_access_table si1133_write_ranges_table =3D {
> +	.yes_ranges	=3D si1133_reg_ranges,
> +	.n_yes_ranges	=3D ARRAY_SIZE(si1133_reg_ranges),
> +	.no_ranges	=3D si1133_reg_ro_ranges,
> +	.n_no_ranges	=3D ARRAY_SIZE(si1133_reg_ro_ranges),
> +};
> +
> +static const struct regmap_access_table si1133_read_ranges_table =3D {
> +	.yes_ranges	=3D si1133_reg_ranges,
> +	.n_yes_ranges	=3D ARRAY_SIZE(si1133_reg_ranges),
> +};
> +
> +static const struct regmap_access_table si1133_precious_table =3D {
> +	.yes_ranges	=3D si1133_precious_ranges,
> +	.n_yes_ranges	=3D ARRAY_SIZE(si1133_precious_ranges),
> +};
> +
> +static const struct regmap_config si1133_regmap_config =3D {
> +	.reg_bits =3D 8,
> +	.val_bits =3D 8,
> +
> +	.max_register =3D 0x2C,
> +
> +	.wr_table =3D &si1133_write_ranges_table,
> +	.rd_table =3D &si1133_read_ranges_table,
> +
> +	.precious_table =3D &si1133_precious_table,
> +};
> +
> +struct si1133_data {
> +	struct regmap *regmap;
> +	struct i2c_client *client;
> +
> +	/* Lock protecting one command at a time can be processed */
> +	struct mutex mutex;
> +
> +	int rsp_seq;
> +	u8 scan_mask;
> +	u8 adc_sens[6];
> +	u8 adc_config[6];
> +
> +	struct completion completion;
> +};
> +
> +struct si1133_coeff {
> +	s16 info;
> +	u16 mag;
> +};
> +
> +struct si1133_lux_coeff {
> +	struct si1133_coeff coeff_high[4];
> +	struct si1133_coeff coeff_low[9];
> +};
> +
> +static const struct si1133_lux_coeff lux_coeff =3D {
> +	{
> +		{  0,   209},
> +		{ 1665,  93},
> +		{ 2064,  65},
> +		{-2671, 234}
> +	},
> +	{
> +		{    0,     0},
> +		{ 1921, 29053},
> +		{-1022, 36363},
> +		{ 2320, 20789},
> +		{ -367, 57909},
> +		{-1774, 38240},
> +		{ -608, 46775},
> +		{-1503, 51831},
> +		{-1886, 58928}
> +	}
> +};
> +
> +int si1133_calculate_polynomial_inner(u32 input, u8 fraction, u16 mag, s=
8 shift)
> +{
> +	return ((input << fraction) / mag) << shift;
> +}
> +
> +int si1133_calculate_output(u32 x, u32 y, u8 x_order, u8 y_order,
static for this an the above (compile checks got this as I'd missed it).
> +			    u8 input_fraction, s8 sign,
> +			    struct si1133_coeff *coeffs)
There is a const discarding issue here that was bothering the compiler.
Fix is obvious so I did it.

> +{
> +	s8 shift;
> +	int x1 =3D 1;
> +	int x2 =3D 1;
> +	int y1 =3D 1;
> +	int y2 =3D 1;
> +
> +	shift =3D ((u16)coeffs->info & 0xFF00) >> 8;
> +	shift ^=3D 0xFF;
> +	shift +=3D 1;
> +	shift =3D -shift;
> +
> +	if (x_order > 0) {
> +		x1 =3D si1133_calculate_polynomial_inner(x, input_fraction,
> +						       coeffs->mag, shift);
> +		if (x_order > 1) {
> +			x2 =3D si1133_calculate_polynomial_inner(x,
> +							       input_fraction,
> +							       coeffs->mag,
> +							       shift);

For these two you calculate the same thing.  Why not just do the x1/x2
after you have calculated it as an assignment?  I.e set x2 to x1
if x_order > 1?

> +		}
> +	}
> +
> +	if (y_order > 0) {
> +		y1 =3D si1133_calculate_polynomial_inner(y, input_fraction,
> +						       coeffs->mag, shift);
> +		if (y_order > 1) {
> +			y2 =3D si1133_calculate_polynomial_inner(y,
> +							       input_fraction,
> +							       coeffs->mag,
> +							       shift);
> +		}
> +	}
> +
> +	return sign * x1 * x2 * y1 * y2;
> +}
> +
> +/*
> + * The algorithm is from:
> + * https://siliconlabs.github.io/Gecko_SDK_Doc/efm32zg/html/si1133_8c_so=
urce.html#l00716
> + */
> +static int si1133_calc_polynomial(u32 x, u32 y, u8 input_fraction, u8 nu=
m_coeff,
> +				  struct si1133_coeff *coeffs)
Another const discard issue here - fixed as per the obvious make it const
option ;)

> +{
> +	u8 x_order, y_order;
> +	u8 counter;
> +	s8 sign;
> +	int output =3D 0;
> +
> +	for (counter =3D 0; counter < num_coeff; counter++) {
> +		if (coeffs->info < 0)
> +			sign =3D -1;
> +		else
> +			sign =3D 1;
> +
> +		x_order =3D get_x_order(coeffs->info);
> +		y_order =3D get_y_order(coeffs->info);
> +
> +		if ((x_order =3D=3D 0) && (y_order =3D=3D 0))
> +			output +=3D
> +			       sign * coeffs->mag << SI1133_LUX_OUTPUT_FRACTION;
> +		else
> +			output +=3D si1133_calculate_output(x, y, x_order,
> +							  y_order,
> +							  input_fraction, sign,
> +							  coeffs);
> +		coeffs++;
> +	}
> +
> +	return abs(output);
> +}
> +
> +static int si1133_cmd_reset_sw(struct si1133_data *data)
> +{
> +	struct device *dev =3D &data->client->dev;
> +	unsigned int resp;
> +	unsigned long timeout;
> +	int err;
> +
> +	err =3D regmap_write(data->regmap, SI1133_REG_COMMAND,
> +			   SI1133_CMD_RESET_SW);
> +	if (err)
> +		return err;
> +
> +	timeout =3D jiffies + msecs_to_jiffies(SI1133_CMD_TIMEOUT_MS);
> +	while (true) {
> +		err =3D regmap_read(data->regmap, SI1133_REG_RESPONSE0, &resp);
> +		if (err =3D=3D -ENXIO) {
> +			usleep_range(SI1133_CMD_MINSLEEP_US_LOW,
> +				     SI1133_CMD_MINSLEEP_US_HIGH);
> +			continue;
> +		}
> +
> +		if ((resp & SI1133_MAX_CMD_CTR) =3D=3D SI1133_MAX_CMD_CTR)
> +			break;
> +
> +		if (time_after(jiffies, timeout)) {
> +			dev_warn(dev, "Timeout on reset ctr resp: %d\n", resp);
> +			return -ETIMEDOUT;
> +		}
> +	}
> +
> +	if (!err)
> +		data->rsp_seq =3D SI1133_MAX_CMD_CTR;
> +
> +	return err;
> +}
> +
> +static int si1133_parse_response_err(struct device *dev, u32 resp, u8 cm=
d)
> +{
> +	resp &=3D 0xF;
> +
> +	switch (resp) {
> +	case SI1133_ERR_OUTPUT_BUFFER_OVERFLOW:
> +		dev_warn(dev, "Output buffer overflow: %#02hhx\n", cmd);
> +		return -EOVERFLOW;
> +	case SI1133_ERR_SATURATION_ADC_OR_OVERFLOW_ACCUMULATION:
> +		dev_warn(dev, "Saturation of the ADC or overflow of accumulation: %#02=
hhx\n",
> +			 cmd);
> +		return -EOVERFLOW;
> +	case SI1133_ERR_INVALID_LOCATION_CMD:
> +		dev_warn(dev,
> +			 "Parameter access to an invalid location: %#02hhx\n",
> +			 cmd);
> +		return -EINVAL;
> +	case SI1133_ERR_INVALID_CMD:
> +		dev_warn(dev, "Invalid command %#02hhx\n", cmd);
> +		return -EINVAL;
> +	default:
> +		dev_warn(dev, "Unknown error %#02hhx\n", cmd);
> +		return -EINVAL;
> +	}
> +}
> +
> +static int si1133_cmd_reset_counter(struct si1133_data *data)
> +{
> +	int err =3D regmap_write(data->regmap, SI1133_REG_COMMAND,
> +			       SI1133_CMD_RESET_CTR);
> +	if (err)
> +		return err;
> +
> +	data->rsp_seq =3D 0;
> +
> +	return 0;
> +}
> +
> +static int si1133_command(struct si1133_data *data, u8 cmd)
> +{
> +	struct device *dev =3D &data->client->dev;
> +	u32 resp;
> +	int err;
> +	int expected_seq;
> +
> +	mutex_lock(&data->mutex);
> +
> +	expected_seq =3D (data->rsp_seq + 1) & SI1133_MAX_CMD_CTR;
> +
> +	if (cmd =3D=3D SI1133_CMD_FORCE)
> +		reinit_completion(&data->completion);
> +
> +	err =3D regmap_write(data->regmap, SI1133_REG_COMMAND, cmd);
> +	if (err) {
> +		dev_warn(dev, "Failed to write command %#02hhx, ret=3D%d\n", cmd,
> +			 err);
> +		goto out;
> +	}
> +
> +	if (cmd =3D=3D SI1133_CMD_FORCE) {
> +		/* wait for irq */
> +		if (!wait_for_completion_timeout(&data->completion,
> +			msecs_to_jiffies(SI1133_COMPLETION_TIMEOUT_MS))) {
> +			err =3D -ETIMEDOUT;
> +			goto out;
> +		}
> +	} else {
> +		err =3D regmap_read_poll_timeout(data->regmap,
> +					       SI1133_REG_RESPONSE0, resp,
> +					       (resp & SI1133_CMD_SEQ_MASK) =3D=3D
> +					       expected_seq ||
> +					       (resp & SI1133_CMD_ERR_MASK),
> +					       SI1133_CMD_MINSLEEP_US_LOW,
> +					       SI1133_CMD_TIMEOUT_MS * 1000);
> +		if (err) {
> +			dev_warn(dev,
> +				 "Failed to read command %#02hhx, ret=3D%d\n",
> +				 cmd, err);
> +			goto out;
> +		}
> +	}
> +
> +	if (resp & SI1133_CMD_ERR_MASK) {
> +		err =3D si1133_parse_response_err(dev, resp, cmd);
> +		si1133_cmd_reset_counter(data);
> +	} else {
> +		data->rsp_seq =3D expected_seq;
> +	}
> +
> +out:
> +	mutex_unlock(&data->mutex);
> +
> +	return err;
> +}
> +
> +static int si1133_param_set(struct si1133_data *data, u8 param, u32 valu=
e)
> +{
> +	int err =3D regmap_write(data->regmap, SI1133_REG_HOSTIN0, value);
> +
> +	if (err)
> +		return err;
> +
> +	return si1133_command(data, SI1133_CMD_PARAM_SET |
> +			      (param & SI1133_CMD_PARAM_MASK));
> +}
> +
> +static int si1133_param_query(struct si1133_data *data, u8 param, u32 *r=
esult)
> +{
> +	int err =3D si1133_command(data, SI1133_CMD_PARAM_QUERY |
> +				 (param & SI1133_CMD_PARAM_MASK));
> +	if (err)
> +		return err;
> +
> +	return regmap_read(data->regmap, SI1133_REG_RESPONSE1, result);
> +}
> +
> +#define SI1133_CHANNEL(_ch, _type) \
> +	.type =3D _type, \
> +	.channel =3D _ch, \
> +	.info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW), \
> +	.info_mask_shared_by_all =3D BIT(IIO_CHAN_INFO_INT_TIME) | \
> +		BIT(IIO_CHAN_INFO_SCALE) | \
> +		BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
> +
> +static const struct iio_chan_spec si1133_channels[] =3D {
> +	{
> +		.type =3D IIO_LIGHT,
> +		.info_mask_separate =3D BIT(IIO_CHAN_INFO_PROCESSED),
> +		.channel =3D 0,
> +	},
> +	{
> +		SI1133_CHANNEL(SI1133_PARAM_ADCMUX_WHITE, IIO_INTENSITY)
> +		.channel2 =3D IIO_MOD_LIGHT_BOTH,
> +	},
> +	{
> +		SI1133_CHANNEL(SI1133_PARAM_ADCMUX_LARGE_WHITE, IIO_INTENSITY)
> +		.channel2 =3D IIO_MOD_LIGHT_BOTH,
> +		.extend_name =3D "large",
> +	},
> +	{
> +		SI1133_CHANNEL(SI1133_PARAM_ADCMUX_SMALL_IR, IIO_INTENSITY)
> +		.extend_name =3D "small",
> +		.modified =3D 1,
> +		.channel2 =3D IIO_MOD_LIGHT_IR,
> +	},
> +	{
> +		SI1133_CHANNEL(SI1133_PARAM_ADCMUX_MED_IR, IIO_INTENSITY)
> +		.modified =3D 1,
> +		.channel2 =3D IIO_MOD_LIGHT_IR,
> +	},
> +	{
> +		SI1133_CHANNEL(SI1133_PARAM_ADCMUX_LARGE_IR, IIO_INTENSITY)
> +		.extend_name =3D "large",
> +		.modified =3D 1,
> +		.channel2 =3D IIO_MOD_LIGHT_IR,
> +	},
> +	{
> +		SI1133_CHANNEL(SI1133_PARAM_ADCMUX_UV, IIO_UVINDEX)
> +	},
> +	{
> +		SI1133_CHANNEL(SI1133_PARAM_ADCMUX_UV_DEEP, IIO_UVINDEX)
> +		.modified =3D 1,
> +		.channel2 =3D IIO_MOD_LIGHT_DUV,
> +	}
> +};
> +
> +static int si1133_get_int_time_index(int milliseconds, int nanoseconds)
> +{
> +	int i;
> +
> +	for (i =3D 0; i < ARRAY_SIZE(si1133_int_time_table); i++) {
> +		if (milliseconds =3D=3D si1133_int_time_table[i][0] &&
> +		    nanoseconds =3D=3D si1133_int_time_table[i][1])
> +			return i;
> +	}
> +	return -EINVAL;
> +}
> +
> +static int si1133_set_integration_time(struct si1133_data *data, u8 adc,
> +				       int milliseconds, int nanoseconds)
> +{
> +	int index;
> +
> +	index =3D si1133_get_int_time_index(milliseconds, nanoseconds);
> +	if (index < 0)
> +		return index;
> +
> +	data->adc_sens[adc] &=3D 0xF0;
> +	data->adc_sens[adc] |=3D index;
> +
> +	return si1133_param_set(data, SI1133_PARAM_REG_ADCSENS(0),
> +				data->adc_sens[adc]);
> +}
> +
> +static int si1133_set_chlist(struct si1133_data *data, u8 scan_mask)
> +{
> +	/* channel list already set, no need to reprogram */
> +	if (data->scan_mask =3D=3D scan_mask)
> +		return 0;
> +
> +	data->scan_mask =3D scan_mask;
> +
> +	return si1133_param_set(data, SI1133_PARAM_REG_CHAN_LIST, scan_mask);
> +}
> +
> +static int si1133_chan_set_adcconfig(struct si1133_data *data, u8 adc,
> +				     u8 adc_config)
> +{
> +	int err;
> +
> +	err =3D si1133_param_set(data, SI1133_PARAM_REG_ADCCONFIG(adc),
> +			       adc_config);
> +	if (err)
> +		return err;
> +
> +	data->adc_config[adc] =3D adc_config;
> +
> +	return 0;
> +}
> +
> +static int si1133_update_adcconfig(struct si1133_data *data, uint8_t adc,
> +				   u8 mask, u8 shift, u8 value)
> +{
> +	u32 adc_config;
> +	int err;
> +
> +	err =3D si1133_param_query(data, SI1133_PARAM_REG_ADCCONFIG(adc),
> +				 &adc_config);
> +	if (err)
> +		return err;
> +
> +	adc_config &=3D ~mask;
> +	adc_config |=3D (value << shift);
> +
> +	return si1133_chan_set_adcconfig(data, adc, adc_config);
> +}
> +
> +static int si1133_set_adcmux(struct si1133_data *data, u8 adc, u8 mux)
> +{
> +	if ((mux & data->adc_config[adc]) =3D=3D mux)
> +		return 0; /* mux already set to correct value */
> +
> +	return si1133_update_adcconfig(data, adc, SI1133_ADCMUX_MASK, 0, mux);
> +}
> +
> +static int si1133_force_measurement(struct si1133_data *data)
> +{
> +	return si1133_command(data, SI1133_CMD_FORCE);
> +}
> +
> +static int si1133_bulk_read(struct si1133_data *data, u8 start_reg, u8 l=
ength,
> +			    u8 *buffer)
> +{
> +	int err;
> +
> +	err =3D si1133_force_measurement(data);
> +	if (err)
> +		return err;
> +
> +	return regmap_bulk_read(data->regmap, start_reg, buffer, length);
> +}
> +
> +static int si1133_measure(struct si1133_data *data,
> +			  struct iio_chan_spec const *chan,
> +			  int *val)
> +{
> +	int err;
> +

Odd extra blank line.

> +	__be16 resp;
> +
> +	err =3D si1133_set_adcmux(data, 0, chan->channel);
> +	if (err)
> +		return err;
> +
> +	/* Deactivate lux measurements if they were active */
> +	err =3D si1133_set_chlist(data, BIT(0));
> +	if (err)
> +		return err;
> +
> +	err =3D si1133_bulk_read(data, SI1133_REG_HOSTOUT(0), sizeof(resp),
> +			       (u8 *)&resp);
> +	if (err)
> +		return err;
> +
> +	*val =3D be16_to_cpu(resp);
> +
> +	return err;
> +}
> +
> +static irqreturn_t si1133_threaded_irq_handler(int irq, void *private)
> +{
> +	struct iio_dev *iio_dev =3D private;
> +	struct si1133_data *data =3D iio_priv(iio_dev);
> +	u32 irq_status;
> +	int err;
> +
> +	err =3D regmap_read(data->regmap, SI1133_REG_IRQ_STATUS, &irq_status);
> +	if (err) {
> +		dev_err_ratelimited(&iio_dev->dev, "Error reading IRQ\n");
> +		goto out;
Interesting question of whether completing makes sense. The data is going t=
o be
garbage, and userspace might never know this.. (it doesn't tend to read the
kernel error log!)

Hmm. Chances of error are very low though, so I don't think it matters huge=
ly.

> +	}
> +
> +	if (irq_status !=3D data->scan_mask)
> +		return IRQ_NONE;
> +
> +out:
> +	complete(&data->completion);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int si1133_scale_to_swgain(int scale_integer, int scale_fractiona=
l)
> +{
> +	scale_integer =3D find_closest(scale_integer, si1133_scale_available,
> +				     ARRAY_SIZE(si1133_scale_available));
> +	if (scale_integer < 0 ||
> +	    scale_integer > ARRAY_SIZE(si1133_scale_available) ||
> +	    scale_fractional !=3D 0)
> +		return -EINVAL;
> +
> +	return scale_integer;
> +}
> +
> +static int si1133_chan_set_adcsens(struct si1133_data *data, u8 adc,
> +				   u8 adc_sens)
> +{
> +	int err;
> +
> +	err =3D si1133_param_set(data, SI1133_PARAM_REG_ADCSENS(adc), adc_sens);
> +	if (err)
> +		return err;
> +
> +	data->adc_sens[adc] =3D adc_sens;
> +
> +	return 0;
> +}
> +
> +static int si1133_update_adcsens(struct si1133_data *data, u8 mask,
> +				 u8 shift, u8 value)
> +{
> +	int err;
> +	u32 adc_sens;
> +
> +	err =3D si1133_param_query(data, SI1133_PARAM_REG_ADCSENS(0),
> +				 &adc_sens);
> +	if (err)
> +		return err;
> +
> +	adc_sens &=3D ~mask;
> +	adc_sens |=3D (value << shift);
> +
> +	return si1133_chan_set_adcsens(data, 0, adc_sens);
> +}
> +
> +static int si1133_get_lux(struct si1133_data *data, int *val)
> +{
> +	int err;
> +	int lux;
> +	u32 high_vis;
> +	u32 low_vis;
> +	u32 ir;
> +	u8 buffer[SI1133_LUX_BUFFER_SIZE];
> +
> +	/* Activate lux channels */
> +	err =3D si1133_set_chlist(data, SI1133_LUX_ADC_MASK);
> +	if (err)
> +		return err;
> +
> +	err =3D si1133_bulk_read(data, SI1133_REG_HOSTOUT(0),
> +			       SI1133_LUX_BUFFER_SIZE, buffer);
> +	if (err)
> +		return err;
> +
> +	high_vis =3D (buffer[0] << 16) | (buffer[1] << 8) | buffer[2];
> +	low_vis =3D (buffer[3] << 16) | (buffer[4] << 8) | buffer[5];
> +	ir =3D (buffer[6] << 16) | (buffer[7] << 8) | buffer[8];
> +
> +	if (high_vis > SI1133_ADC_THRESHOLD || ir > SI1133_ADC_THRESHOLD)
> +		lux =3D si1133_calc_polynomial(high_vis, ir,
> +					     SI1133_INPUT_FRACTION_HIGH,
> +					     ARRAY_SIZE(lux_coeff.coeff_high),
> +					     &lux_coeff.coeff_high[0]);
> +	else
> +		lux =3D si1133_calc_polynomial(low_vis, ir,
> +					     SI1133_INPUT_FRACTION_LOW,
> +					     ARRAY_SIZE(lux_coeff.coeff_low),
> +					     &lux_coeff.coeff_low[0]);
> +
> +	*val =3D lux >> SI1133_LUX_OUTPUT_FRACTION;
> +
> +	return err;
> +}
> +
> +static int si1133_read_raw(struct iio_dev *iio_dev,
> +			   struct iio_chan_spec const *chan,
> +			   int *val, int *val2, long mask)
> +{
> +	struct si1133_data *data =3D iio_priv(iio_dev);
> +	u8 adc_sens =3D data->adc_sens[0];
> +	int err;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_PROCESSED:
> +		switch (chan->type) {
> +		case IIO_LIGHT:
> +			err =3D si1133_get_lux(data, val);
> +			if (err)
> +				return err;
> +
> +			return IIO_VAL_INT;
> +		default:
> +			return -EINVAL;
> +		}
> +	case IIO_CHAN_INFO_RAW:
> +		switch (chan->type) {
> +		case IIO_INTENSITY:
> +		case IIO_UVINDEX:
> +			err =3D si1133_measure(data, chan, val);
> +			if (err)
> +				return err;
> +
> +			return IIO_VAL_INT;
> +		default:
> +			return -EINVAL;
> +		}
> +	case IIO_CHAN_INFO_INT_TIME:
> +		switch (chan->type) {
> +		case IIO_INTENSITY:
> +		case IIO_UVINDEX:
> +			adc_sens &=3D SI1133_ADCSENS_HW_GAIN_MASK;
> +
> +			*val =3D si1133_int_time_table[adc_sens][0];
> +			*val2 =3D si1133_int_time_table[adc_sens][1];
> +			return IIO_VAL_INT_PLUS_MICRO;
> +		default:
> +			return -EINVAL;
> +		}
> +	case IIO_CHAN_INFO_SCALE:
> +		switch (chan->type) {
> +		case IIO_INTENSITY:
> +		case IIO_UVINDEX:
> +			adc_sens &=3D SI1133_ADCSENS_SCALE_MASK;
> +			adc_sens >>=3D SI1133_ADCSENS_SCALE_SHIFT;
> +
> +			*val =3D BIT(adc_sens);
> +
> +			return IIO_VAL_INT;
> +		default:
> +			return -EINVAL;
> +		}
> +	case IIO_CHAN_INFO_HARDWAREGAIN:
> +		switch (chan->type) {
> +		case IIO_INTENSITY:
> +		case IIO_UVINDEX:
> +			adc_sens >>=3D SI1133_ADCSENS_HSIG_SHIFT;
> +
> +			*val =3D adc_sens;
> +
> +			return IIO_VAL_INT;
> +		default:
> +			return -EINVAL;
> +		}
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int si1133_write_raw(struct iio_dev *iio_dev,
> +			    struct iio_chan_spec const *chan,
> +			    int val, int val2, long mask)
> +{
> +	struct si1133_data *data =3D iio_priv(iio_dev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SCALE:
> +		switch (chan->type) {
> +		case IIO_INTENSITY:
> +		case IIO_UVINDEX:
> +			val =3D si1133_scale_to_swgain(val, val2);
> +			if (val < 0)
> +				return val;
> +
> +			return si1133_update_adcsens(data,
> +						     SI1133_ADCSENS_SCALE_MASK,
> +						     SI1133_ADCSENS_SCALE_SHIFT,
> +						     val);
> +		default:
> +			return -EINVAL;
> +		}
> +	case IIO_CHAN_INFO_INT_TIME:
> +		return si1133_set_integration_time(data, 0, val, val2);
> +	case IIO_CHAN_INFO_HARDWAREGAIN:
> +		switch (chan->type) {
> +		case IIO_INTENSITY:
> +		case IIO_UVINDEX:
> +			if (val !=3D 0 || val !=3D 1)
> +				return -EINVAL;
> +
> +			return si1133_update_adcsens(data,
> +						     SI1133_ADCSENS_HSIG_MASK,
> +						     SI1133_ADCSENS_HSIG_SHIFT,
> +						     val);
> +		default:
> +			return -EINVAL;
> +		}
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static struct attribute *si1133_attributes[] =3D {
> +	&iio_const_attr_integration_time_available.dev_attr.attr,
> +	&iio_const_attr_scale_available.dev_attr.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group si1133_attribute_group =3D {
> +	.attrs =3D si1133_attributes,
> +};
> +
> +static const struct iio_info si1133_info =3D {
> +	.read_raw =3D si1133_read_raw,
> +	.write_raw =3D si1133_write_raw,
> +	.attrs =3D &si1133_attribute_group,
> +};
> +
> +/*
> + * si1133_init_lux_channels - Configure 3 different channels(adc) (1,2 a=
nd 3)
> + * The channel configuration for the lux measurement was taken from :
> + * https://siliconlabs.github.io/Gecko_SDK_Doc/efm32zg/html/si1133_8c_so=
urce.html#l00578
> + *
> + * Reserved the channel 0 for the other raw measurements
> + */
> +static int si1133_init_lux_channels(struct si1133_data *data)
> +{
> +	int err;
> +
> +	err =3D si1133_chan_set_adcconfig(data, 1,
> +					SI1133_ADCCONFIG_DECIM_RATE(1) |
> +					SI1133_PARAM_ADCMUX_LARGE_WHITE);
> +	if (err)
> +		return err;
> +
> +	err =3D si1133_param_set(data, SI1133_PARAM_REG_ADCPOST(1),
> +			       SI1133_ADCPOST_24BIT_EN |
> +			       SI1133_ADCPOST_POSTSHIFT_BITQTY(0));
> +	if (err)
> +		return err;
> +	err =3D si1133_chan_set_adcsens(data, 1, SI1133_ADCSENS_HSIG_MASK |
> +				      SI1133_ADCSENS_NB_MEAS(64) | _48_8_us);
> +	if (err)
> +		return err;
> +
> +	err =3D si1133_chan_set_adcconfig(data, 2,
> +					SI1133_ADCCONFIG_DECIM_RATE(1) |
> +					SI1133_PARAM_ADCMUX_LARGE_WHITE);
> +	if (err)
> +		return err;
> +
> +	err =3D si1133_param_set(data, SI1133_PARAM_REG_ADCPOST(2),
> +			       SI1133_ADCPOST_24BIT_EN |
> +			       SI1133_ADCPOST_POSTSHIFT_BITQTY(2));
> +	if (err)
> +		return err;
> +
> +	err =3D si1133_chan_set_adcsens(data, 2, SI1133_ADCSENS_HSIG_MASK |
> +				      SI1133_ADCSENS_NB_MEAS(1) | _3_120_0_us);
> +	if (err)
> +		return err;
> +
> +	err =3D si1133_chan_set_adcconfig(data, 3,
> +					SI1133_ADCCONFIG_DECIM_RATE(1) |
> +					SI1133_PARAM_ADCMUX_MED_IR);
> +	if (err)
> +		return err;
> +
> +	err =3D si1133_param_set(data, SI1133_PARAM_REG_ADCPOST(3),
> +			       SI1133_ADCPOST_24BIT_EN |
> +			       SI1133_ADCPOST_POSTSHIFT_BITQTY(2));
> +	if (err)
> +		return err;
> +
> +	err =3D si1133_chan_set_adcsens(data, 3, SI1133_ADCSENS_HSIG_MASK |
> +				      SI1133_ADCSENS_NB_MEAS(64) | _48_8_us);
> +	if (err)
> +		return err;

This if section has no purpose. If it's all that I find I'll clear this
up whilst applying (I review backwards btw)

> +
> +	return err;
> +}
> +
> +static int si1133_initialize(struct si1133_data *data)
> +{
> +	int err;
> +
> +	err =3D si1133_cmd_reset_sw(data);
> +	if (err)
> +		return err;
> +
> +	/* Turn off autonomous mode */
> +	err =3D si1133_param_set(data, SI1133_REG_MEAS_RATE, 0);
> +	if (err)
> +		return err;
> +
> +	err =3D si1133_init_lux_channels(data);
> +	if (err)
> +		return err;
> +
> +	return regmap_write(data->regmap, SI1133_REG_IRQ_ENABLE,
> +			    SI1133_IRQ_CHANNEL_ENABLE);
> +}
> +
> +static int si1133_validate_ids(struct iio_dev *iio_dev)
> +{
> +	struct si1133_data *data =3D iio_priv(iio_dev);
> +
> +	unsigned int part_id, rev_id, mfr_id;
> +	int err;
> +
> +	err =3D regmap_read(data->regmap, SI1133_REG_PART_ID, &part_id);
> +	if (err)
> +		return err;
> +
> +	err =3D regmap_read(data->regmap, SI1133_REG_REV_ID, &rev_id);
> +	if (err)
> +		return err;
> +
> +	err =3D regmap_read(data->regmap, SI1133_REG_MFR_ID, &mfr_id);
> +	if (err)
> +		return err;
> +
> +	dev_info(&iio_dev->dev,
> +		 "Device ID part %#02hhx rev %#02hhx mfr %#02hhx\n",
> +		 part_id, rev_id, mfr_id);
> +	if (part_id !=3D SI1133_PART_ID) {
> +		dev_err(&iio_dev->dev,
> +			"Part ID mismatch got %#02hhx, expected %#02x\n",
> +			part_id, SI1133_PART_ID);
> +		return -ENODEV;
> +	}
> +
> +	return 0;
> +}
> +
> +static int si1133_probe(struct i2c_client *client,
> +			const struct i2c_device_id *id)
> +{
> +	struct si1133_data *data;
> +	struct iio_dev *iio_dev;
> +	int err;
> +
> +	iio_dev =3D devm_iio_device_alloc(&client->dev, sizeof(*data));
> +	if (!iio_dev)
> +		return -ENOMEM;
> +
> +	data =3D iio_priv(iio_dev);
> +
> +	init_completion(&data->completion);
> +
> +	data->regmap =3D devm_regmap_init_i2c(client, &si1133_regmap_config);
> +	if (IS_ERR(data->regmap)) {
> +		err =3D PTR_ERR(data->regmap);
> +		dev_err(&client->dev, "Failed to initialise regmap: %d\n", err);
> +		return err;
> +	}
> +
> +	i2c_set_clientdata(client, iio_dev);
> +	data->client =3D client;
> +
> +	iio_dev->dev.parent =3D &client->dev;
> +	iio_dev->name =3D id->name;
> +	iio_dev->channels =3D si1133_channels;
> +	iio_dev->num_channels =3D ARRAY_SIZE(si1133_channels);
> +	iio_dev->info =3D &si1133_info;
> +	iio_dev->modes =3D INDIO_DIRECT_MODE;
> +
> +	mutex_init(&data->mutex);
> +
> +	err =3D si1133_validate_ids(iio_dev);
> +	if (err)
> +		return err;
> +
> +	err =3D si1133_initialize(data);
> +	if (err) {
> +		dev_err(&client->dev,
> +			"Error when initializing chip: %d\n", err);
> +		return err;
> +	}
> +
> +	if (!client->irq) {
> +		dev_err(&client->dev,
> +			"Required interrupt not provided, cannot proceed\n");
> +		return -EINVAL;
> +	}
> +
> +	err =3D devm_request_threaded_irq(&client->dev, client->irq,
> +					NULL,
> +					si1133_threaded_irq_handler,
> +					IRQF_ONESHOT | IRQF_SHARED,
> +					client->name, iio_dev);
> +	if (err) {
> +		dev_warn(&client->dev, "Request irq %d failed: %i\n",
> +			 client->irq, err);
> +		return err;
> +	}
> +
> +	return devm_iio_device_register(&client->dev, iio_dev);
> +}
> +
> +static const struct i2c_device_id si1133_ids[] =3D {
> +	{ "si1133", 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, si1133_ids);
> +
> +static struct i2c_driver si1133_driver =3D {
> +	.driver =3D {
> +	    .name   =3D "si1133",
> +	},
> +	.probe  =3D si1133_probe,
> +	.id_table =3D si1133_ids,
> +};
> +
> +module_i2c_driver(si1133_driver);
> +
> +MODULE_AUTHOR("Maxime Roussin-Belanger <maxime.roussinbelanger@gmail.com=
>");
> +MODULE_DESCRIPTION("Silabs SI1133, UV index sensor and ambient light sen=
sor driver");
> +MODULE_LICENSE("GPL");


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

* Re: [PATCH v3 1/2] iio: Add modifier for DUV light
  2018-07-19 20:26 [PATCH v3 1/2] iio: Add modifier for DUV light Maxime Roussin-Bélanger
  2018-07-19 20:26 ` [PATCH v3 2/2] iio: light: introduce si1133 Maxime Roussin-Bélanger
@ 2018-07-21 16:15 ` Jonathan Cameron
  1 sibling, 0 replies; 4+ messages in thread
From: Jonathan Cameron @ 2018-07-21 16:15 UTC (permalink / raw
  To: Maxime Roussin-Bélanger
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	linux-iio, Jean-Francois Dagenais

On Thu, 19 Jul 2018 16:26:24 -0400
Maxime Roussin-B=C3=A9langer <maxime.roussinbelanger@gmail.com> wrote:

> Signed-off-by: Maxime Roussin-B=C3=A9langer <maxime.roussinbelanger@gmail=
.com>

Applied, Thanks,

Jonathan
> ---
>  Documentation/ABI/testing/sysfs-bus-iio | 4 +++-
>  drivers/iio/industrialio-core.c         | 1 +
>  include/uapi/linux/iio/types.h          | 1 +
>  tools/iio/iio_event_monitor.c           | 2 ++
>  4 files changed, 7 insertions(+), 1 deletion(-)
>=20
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/=
testing/sysfs-bus-iio
> index fee35c00cc4e..23b213f87cf5 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -1263,13 +1263,15 @@ What:		/sys/.../iio:deviceX/in_intensityY_raw
>  What:		/sys/.../iio:deviceX/in_intensityY_ir_raw
>  What:		/sys/.../iio:deviceX/in_intensityY_both_raw
>  What:		/sys/.../iio:deviceX/in_intensityY_uv_raw
> +What:		/sys/.../iio:deviceX/in_intensityY_duv_raw
>  KernelVersion:	3.4
>  Contact:	linux-iio@vger.kernel.org
>  Description:
>  		Unit-less light intensity. Modifiers both and ir indicate
>  		that measurements contains visible and infrared light
>  		components or just infrared light, respectively. Modifier uv indicates
> -		that measurements contain ultraviolet light components.
> +		that measurements contain ultraviolet light components. Modifier duv
> +		indicates that measurements contain deep ultraviolet light components.
> =20
>  What:		/sys/.../iio:deviceX/in_uvindex_input
>  KernelVersion:	4.6
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-c=
ore.c
> index fc340ed3dca1..66aba36d7fe1 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -104,6 +104,7 @@ static const char * const iio_modifier_names[] =3D {
>  	[IIO_MOD_LIGHT_GREEN] =3D "green",
>  	[IIO_MOD_LIGHT_BLUE] =3D "blue",
>  	[IIO_MOD_LIGHT_UV] =3D "uv",
> +	[IIO_MOD_LIGHT_DUV] =3D "duv",
>  	[IIO_MOD_QUATERNION] =3D "quaternion",
>  	[IIO_MOD_TEMP_AMBIENT] =3D "ambient",
>  	[IIO_MOD_TEMP_OBJECT] =3D "object",
> diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/type=
s.h
> index 22e5e589a274..bf037a267c7d 100644
> --- a/include/uapi/linux/iio/types.h
> +++ b/include/uapi/linux/iio/types.h
> @@ -80,6 +80,7 @@ enum iio_modifier {
>  	IIO_MOD_CO2,
>  	IIO_MOD_VOC,
>  	IIO_MOD_LIGHT_UV,
> +	IIO_MOD_LIGHT_DUV,
>  };
> =20
>  enum iio_event_type {
> diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c
> index d9b7e0f306c6..328c66d8a892 100644
> --- a/tools/iio/iio_event_monitor.c
> +++ b/tools/iio/iio_event_monitor.c
> @@ -95,6 +95,7 @@ static const char * const iio_modifier_names[] =3D {
>  	[IIO_MOD_LIGHT_GREEN] =3D "green",
>  	[IIO_MOD_LIGHT_BLUE] =3D "blue",
>  	[IIO_MOD_LIGHT_UV] =3D "uv",
> +	[IIO_MOD_LIGHT_DUV] =3D "duv",
>  	[IIO_MOD_QUATERNION] =3D "quaternion",
>  	[IIO_MOD_TEMP_AMBIENT] =3D "ambient",
>  	[IIO_MOD_TEMP_OBJECT] =3D "object",
> @@ -176,6 +177,7 @@ static bool event_is_known(struct iio_event_data *eve=
nt)
>  	case IIO_MOD_LIGHT_GREEN:
>  	case IIO_MOD_LIGHT_BLUE:
>  	case IIO_MOD_LIGHT_UV:
> +	case IIO_MOD_LIGHT_DUV:
>  	case IIO_MOD_QUATERNION:
>  	case IIO_MOD_TEMP_AMBIENT:
>  	case IIO_MOD_TEMP_OBJECT:


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

end of thread, other threads:[~2018-07-21 17:08 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-07-19 20:26 [PATCH v3 1/2] iio: Add modifier for DUV light Maxime Roussin-Bélanger
2018-07-19 20:26 ` [PATCH v3 2/2] iio: light: introduce si1133 Maxime Roussin-Bélanger
2018-07-21 16:06   ` Jonathan Cameron
2018-07-21 16:15 ` [PATCH v3 1/2] iio: Add modifier for DUV light Jonathan Cameron

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.