xref: /linux/drivers/iio/light/opt4060.c (revision c26f4fbd58375bd6ef74f95eb73d61762ad97c59)
10c6db450SPer-Daniel Olsson // SPDX-License-Identifier: GPL-2.0-only
20c6db450SPer-Daniel Olsson /*
30c6db450SPer-Daniel Olsson  * Copyright (C) 2024 Axis Communications AB
40c6db450SPer-Daniel Olsson  *
50c6db450SPer-Daniel Olsson  * Datasheet: https://www.ti.com/lit/gpn/opt4060
60c6db450SPer-Daniel Olsson  *
70c6db450SPer-Daniel Olsson  * Device driver for the Texas Instruments OPT4060 RGBW Color Sensor.
80c6db450SPer-Daniel Olsson  */
90c6db450SPer-Daniel Olsson 
100c6db450SPer-Daniel Olsson #include <linux/bitfield.h>
110c6db450SPer-Daniel Olsson #include <linux/i2c.h>
120c6db450SPer-Daniel Olsson #include <linux/iio/iio.h>
130c6db450SPer-Daniel Olsson #include <linux/math64.h>
140c6db450SPer-Daniel Olsson #include <linux/units.h>
150c6db450SPer-Daniel Olsson #include <linux/limits.h>
160c6db450SPer-Daniel Olsson #include <linux/module.h>
170c6db450SPer-Daniel Olsson #include <linux/property.h>
180c6db450SPer-Daniel Olsson #include <linux/regmap.h>
190c6db450SPer-Daniel Olsson #include <linux/mutex.h>
200c6db450SPer-Daniel Olsson #include <linux/regulator/consumer.h>
210c6db450SPer-Daniel Olsson #include <linux/iio/events.h>
220c6db450SPer-Daniel Olsson #include <linux/iio/trigger.h>
230c6db450SPer-Daniel Olsson #include <linux/iio/trigger_consumer.h>
240c6db450SPer-Daniel Olsson #include <linux/iio/triggered_buffer.h>
250c6db450SPer-Daniel Olsson 
260c6db450SPer-Daniel Olsson /* OPT4060 register set */
270c6db450SPer-Daniel Olsson #define OPT4060_RED_MSB				0x00
280c6db450SPer-Daniel Olsson #define OPT4060_RED_LSB				0x01
290c6db450SPer-Daniel Olsson #define OPT4060_GREEN_MSB			0x02
300c6db450SPer-Daniel Olsson #define OPT4060_GREEN_LSB			0x03
310c6db450SPer-Daniel Olsson #define OPT4060_BLUE_MSB			0x04
320c6db450SPer-Daniel Olsson #define OPT4060_BLUE_LSB			0x05
330c6db450SPer-Daniel Olsson #define OPT4060_CLEAR_MSB			0x06
340c6db450SPer-Daniel Olsson #define OPT4060_CLEAR_LSB			0x07
350c6db450SPer-Daniel Olsson #define OPT4060_THRESHOLD_LOW			0x08
360c6db450SPer-Daniel Olsson #define OPT4060_THRESHOLD_HIGH			0x09
370c6db450SPer-Daniel Olsson #define OPT4060_CTRL				0x0a
380c6db450SPer-Daniel Olsson #define OPT4060_INT_CTRL			0x0b
390c6db450SPer-Daniel Olsson #define OPT4060_RES_CTRL			0x0c
400c6db450SPer-Daniel Olsson #define OPT4060_DEVICE_ID			0x11
410c6db450SPer-Daniel Olsson 
420c6db450SPer-Daniel Olsson /* OPT4060 register mask */
430c6db450SPer-Daniel Olsson #define OPT4060_EXPONENT_MASK			GENMASK(15, 12)
440c6db450SPer-Daniel Olsson #define OPT4060_MSB_MASK			GENMASK(11, 0)
450c6db450SPer-Daniel Olsson #define OPT4060_LSB_MASK			GENMASK(15, 8)
460c6db450SPer-Daniel Olsson #define OPT4060_COUNTER_MASK			GENMASK(7, 4)
470c6db450SPer-Daniel Olsson #define OPT4060_CRC_MASK			GENMASK(3, 0)
480c6db450SPer-Daniel Olsson 
490c6db450SPer-Daniel Olsson /* OPT4060 device id mask */
500c6db450SPer-Daniel Olsson #define OPT4060_DEVICE_ID_MASK			GENMASK(11, 0)
510c6db450SPer-Daniel Olsson 
520c6db450SPer-Daniel Olsson /* OPT4060 control register masks */
530c6db450SPer-Daniel Olsson #define OPT4060_CTRL_QWAKE_MASK			BIT(15)
540c6db450SPer-Daniel Olsson #define OPT4060_CTRL_RANGE_MASK			GENMASK(13, 10)
550c6db450SPer-Daniel Olsson #define OPT4060_CTRL_CONV_TIME_MASK		GENMASK(9, 6)
560c6db450SPer-Daniel Olsson #define OPT4060_CTRL_OPER_MODE_MASK		GENMASK(5, 4)
570c6db450SPer-Daniel Olsson #define OPT4060_CTRL_LATCH_MASK			BIT(3)
580c6db450SPer-Daniel Olsson #define OPT4060_CTRL_INT_POL_MASK		BIT(2)
590c6db450SPer-Daniel Olsson #define OPT4060_CTRL_FAULT_COUNT_MASK		GENMASK(1, 0)
600c6db450SPer-Daniel Olsson 
610c6db450SPer-Daniel Olsson /* OPT4060 interrupt control register masks */
620c6db450SPer-Daniel Olsson #define OPT4060_INT_CTRL_THRESH_SEL		GENMASK(6, 5)
630c6db450SPer-Daniel Olsson #define OPT4060_INT_CTRL_OUTPUT			BIT(4)
640c6db450SPer-Daniel Olsson #define OPT4060_INT_CTRL_INT_CFG		GENMASK(3, 2)
650c6db450SPer-Daniel Olsson #define OPT4060_INT_CTRL_THRESHOLD		0x0
660c6db450SPer-Daniel Olsson #define OPT4060_INT_CTRL_NEXT_CH		0x1
670c6db450SPer-Daniel Olsson #define OPT4060_INT_CTRL_ALL_CH			0x3
680c6db450SPer-Daniel Olsson 
690c6db450SPer-Daniel Olsson /* OPT4060 result control register masks */
700c6db450SPer-Daniel Olsson #define OPT4060_RES_CTRL_OVERLOAD		BIT(3)
710c6db450SPer-Daniel Olsson #define OPT4060_RES_CTRL_CONV_READY		BIT(2)
720c6db450SPer-Daniel Olsson #define OPT4060_RES_CTRL_FLAG_H			BIT(1)
730c6db450SPer-Daniel Olsson #define OPT4060_RES_CTRL_FLAG_L			BIT(0)
740c6db450SPer-Daniel Olsson 
750c6db450SPer-Daniel Olsson /* OPT4060 constants */
760c6db450SPer-Daniel Olsson #define OPT4060_DEVICE_ID_VAL			0x821
770c6db450SPer-Daniel Olsson 
780c6db450SPer-Daniel Olsson /* OPT4060 operating modes */
790c6db450SPer-Daniel Olsson #define OPT4060_CTRL_OPER_MODE_OFF		0x0
800c6db450SPer-Daniel Olsson #define OPT4060_CTRL_OPER_MODE_FORCED		0x1
810c6db450SPer-Daniel Olsson #define OPT4060_CTRL_OPER_MODE_ONE_SHOT		0x2
820c6db450SPer-Daniel Olsson #define OPT4060_CTRL_OPER_MODE_CONTINUOUS	0x3
830c6db450SPer-Daniel Olsson 
840c6db450SPer-Daniel Olsson /* OPT4060 conversion control register definitions */
850c6db450SPer-Daniel Olsson #define OPT4060_CTRL_CONVERSION_0_6MS		0x0
860c6db450SPer-Daniel Olsson #define OPT4060_CTRL_CONVERSION_1MS		0x1
870c6db450SPer-Daniel Olsson #define OPT4060_CTRL_CONVERSION_1_8MS		0x2
880c6db450SPer-Daniel Olsson #define OPT4060_CTRL_CONVERSION_3_4MS		0x3
890c6db450SPer-Daniel Olsson #define OPT4060_CTRL_CONVERSION_6_5MS		0x4
900c6db450SPer-Daniel Olsson #define OPT4060_CTRL_CONVERSION_12_7MS		0x5
910c6db450SPer-Daniel Olsson #define OPT4060_CTRL_CONVERSION_25MS		0x6
920c6db450SPer-Daniel Olsson #define OPT4060_CTRL_CONVERSION_50MS		0x7
930c6db450SPer-Daniel Olsson #define OPT4060_CTRL_CONVERSION_100MS		0x8
940c6db450SPer-Daniel Olsson #define OPT4060_CTRL_CONVERSION_200MS		0x9
950c6db450SPer-Daniel Olsson #define OPT4060_CTRL_CONVERSION_400MS		0xa
960c6db450SPer-Daniel Olsson #define OPT4060_CTRL_CONVERSION_800MS		0xb
970c6db450SPer-Daniel Olsson 
980c6db450SPer-Daniel Olsson /* OPT4060 fault count control register definitions */
990c6db450SPer-Daniel Olsson #define OPT4060_CTRL_FAULT_COUNT_1		0x0
1000c6db450SPer-Daniel Olsson #define OPT4060_CTRL_FAULT_COUNT_2		0x1
1010c6db450SPer-Daniel Olsson #define OPT4060_CTRL_FAULT_COUNT_4		0x2
1020c6db450SPer-Daniel Olsson #define OPT4060_CTRL_FAULT_COUNT_8		0x3
1030c6db450SPer-Daniel Olsson 
1040c6db450SPer-Daniel Olsson /* OPT4060 scale light level range definitions */
1050c6db450SPer-Daniel Olsson #define OPT4060_CTRL_LIGHT_SCALE_AUTO		12
1060c6db450SPer-Daniel Olsson 
1070c6db450SPer-Daniel Olsson /* OPT4060 default values */
1080c6db450SPer-Daniel Olsson #define OPT4060_DEFAULT_CONVERSION_TIME OPT4060_CTRL_CONVERSION_50MS
1090c6db450SPer-Daniel Olsson 
1100c6db450SPer-Daniel Olsson /*
1110c6db450SPer-Daniel Olsson  * enum opt4060_chan_type - OPT4060 channel types
1120c6db450SPer-Daniel Olsson  * @OPT4060_RED:	Red channel.
1130c6db450SPer-Daniel Olsson  * @OPT4060_GREEN:	Green channel.
1140c6db450SPer-Daniel Olsson  * @OPT4060_BLUE:	Blue channel.
1150c6db450SPer-Daniel Olsson  * @OPT4060_CLEAR:	Clear (white) channel.
1160c6db450SPer-Daniel Olsson  * @OPT4060_ILLUM:	Calculated illuminance channel.
1170c6db450SPer-Daniel Olsson  * @OPT4060_NUM_CHANS:	Number of channel types.
1180c6db450SPer-Daniel Olsson  */
1190c6db450SPer-Daniel Olsson enum opt4060_chan_type {
1200c6db450SPer-Daniel Olsson 	OPT4060_RED,
1210c6db450SPer-Daniel Olsson 	OPT4060_GREEN,
1220c6db450SPer-Daniel Olsson 	OPT4060_BLUE,
1230c6db450SPer-Daniel Olsson 	OPT4060_CLEAR,
1240c6db450SPer-Daniel Olsson 	OPT4060_ILLUM,
1250c6db450SPer-Daniel Olsson 	OPT4060_NUM_CHANS
1260c6db450SPer-Daniel Olsson };
1270c6db450SPer-Daniel Olsson 
1280c6db450SPer-Daniel Olsson struct opt4060_chip {
1290c6db450SPer-Daniel Olsson 	struct regmap *regmap;
1300c6db450SPer-Daniel Olsson 	struct device *dev;
1310c6db450SPer-Daniel Olsson 	struct iio_trigger *trig;
1320c6db450SPer-Daniel Olsson 	u8 int_time;
1330c6db450SPer-Daniel Olsson 	int irq;
1340c6db450SPer-Daniel Olsson 	/*
1350c6db450SPer-Daniel Olsson 	 * Mutex for protecting sensor irq settings. Switching between interrupt
1360c6db450SPer-Daniel Olsson 	 * on each sample and on thresholds needs to be synchronized.
1370c6db450SPer-Daniel Olsson 	 */
1380c6db450SPer-Daniel Olsson 	struct mutex irq_setting_lock;
1390c6db450SPer-Daniel Olsson 	/*
1400c6db450SPer-Daniel Olsson 	 * Mutex for protecting event enabling.
1410c6db450SPer-Daniel Olsson 	 */
1420c6db450SPer-Daniel Olsson 	struct mutex event_enabling_lock;
1430c6db450SPer-Daniel Olsson 	struct completion completion;
1440c6db450SPer-Daniel Olsson 	bool thresh_event_lo_active;
1450c6db450SPer-Daniel Olsson 	bool thresh_event_hi_active;
1460c6db450SPer-Daniel Olsson };
1470c6db450SPer-Daniel Olsson 
1480c6db450SPer-Daniel Olsson struct opt4060_channel_factor {
1490c6db450SPer-Daniel Olsson 	u32 mul;
1500c6db450SPer-Daniel Olsson 	u32 div;
1510c6db450SPer-Daniel Olsson };
1520c6db450SPer-Daniel Olsson 
1530c6db450SPer-Daniel Olsson static const int opt4060_int_time_available[][2] = {
1540c6db450SPer-Daniel Olsson 	{ 0,    600 },
1550c6db450SPer-Daniel Olsson 	{ 0,   1000 },
1560c6db450SPer-Daniel Olsson 	{ 0,   1800 },
1570c6db450SPer-Daniel Olsson 	{ 0,   3400 },
1580c6db450SPer-Daniel Olsson 	{ 0,   6500 },
1590c6db450SPer-Daniel Olsson 	{ 0,  12700 },
1600c6db450SPer-Daniel Olsson 	{ 0,  25000 },
1610c6db450SPer-Daniel Olsson 	{ 0,  50000 },
1620c6db450SPer-Daniel Olsson 	{ 0, 100000 },
1630c6db450SPer-Daniel Olsson 	{ 0, 200000 },
1640c6db450SPer-Daniel Olsson 	{ 0, 400000 },
1650c6db450SPer-Daniel Olsson 	{ 0, 800000 },
1660c6db450SPer-Daniel Olsson };
1670c6db450SPer-Daniel Olsson 
1680c6db450SPer-Daniel Olsson /*
1690c6db450SPer-Daniel Olsson  * Conversion time is integration time + time to set register
1700c6db450SPer-Daniel Olsson  * this is used as integration time.
1710c6db450SPer-Daniel Olsson  */
1720c6db450SPer-Daniel Olsson static const int opt4060_int_time_reg[][2] = {
1730c6db450SPer-Daniel Olsson 	{    600,  OPT4060_CTRL_CONVERSION_0_6MS  },
1740c6db450SPer-Daniel Olsson 	{   1000,  OPT4060_CTRL_CONVERSION_1MS    },
1750c6db450SPer-Daniel Olsson 	{   1800,  OPT4060_CTRL_CONVERSION_1_8MS  },
1760c6db450SPer-Daniel Olsson 	{   3400,  OPT4060_CTRL_CONVERSION_3_4MS  },
1770c6db450SPer-Daniel Olsson 	{   6500,  OPT4060_CTRL_CONVERSION_6_5MS  },
1780c6db450SPer-Daniel Olsson 	{  12700,  OPT4060_CTRL_CONVERSION_12_7MS },
1790c6db450SPer-Daniel Olsson 	{  25000,  OPT4060_CTRL_CONVERSION_25MS   },
1800c6db450SPer-Daniel Olsson 	{  50000,  OPT4060_CTRL_CONVERSION_50MS   },
1810c6db450SPer-Daniel Olsson 	{ 100000,  OPT4060_CTRL_CONVERSION_100MS  },
1820c6db450SPer-Daniel Olsson 	{ 200000,  OPT4060_CTRL_CONVERSION_200MS  },
1830c6db450SPer-Daniel Olsson 	{ 400000,  OPT4060_CTRL_CONVERSION_400MS  },
1840c6db450SPer-Daniel Olsson 	{ 800000,  OPT4060_CTRL_CONVERSION_800MS  },
1850c6db450SPer-Daniel Olsson };
1860c6db450SPer-Daniel Olsson 
opt4060_als_time_to_index(const u32 als_integration_time)1870c6db450SPer-Daniel Olsson static int opt4060_als_time_to_index(const u32 als_integration_time)
1880c6db450SPer-Daniel Olsson {
1890c6db450SPer-Daniel Olsson 	int i;
1900c6db450SPer-Daniel Olsson 
1910c6db450SPer-Daniel Olsson 	for (i = 0; i < ARRAY_SIZE(opt4060_int_time_available); i++) {
1920c6db450SPer-Daniel Olsson 		if (als_integration_time == opt4060_int_time_available[i][1])
1930c6db450SPer-Daniel Olsson 			return i;
1940c6db450SPer-Daniel Olsson 	}
1950c6db450SPer-Daniel Olsson 
1960c6db450SPer-Daniel Olsson 	return -EINVAL;
1970c6db450SPer-Daniel Olsson }
1980c6db450SPer-Daniel Olsson 
opt4060_calculate_crc(u8 exp,u32 mantissa,u8 count)1990c6db450SPer-Daniel Olsson static u8 opt4060_calculate_crc(u8 exp, u32 mantissa, u8 count)
2000c6db450SPer-Daniel Olsson {
2010c6db450SPer-Daniel Olsson 	u8 crc;
2020c6db450SPer-Daniel Olsson 
2030c6db450SPer-Daniel Olsson 	/*
2040c6db450SPer-Daniel Olsson 	 * Calculates a 4-bit CRC from a 20-bit mantissa, 4-bit exponent and a 4-bit counter.
2050c6db450SPer-Daniel Olsson 	 * crc[0] = XOR(mantissa[19:0], exp[3:0], count[3:0])
2060c6db450SPer-Daniel Olsson 	 * crc[1] = XOR(mantissa[1,3,5,7,9,11,13,15,17,19], exp[1,3], count[1,3])
2070c6db450SPer-Daniel Olsson 	 * crc[2] = XOR(mantissa[3,7,11,15,19], exp[3], count[3])
2080c6db450SPer-Daniel Olsson 	 * crc[3] = XOR(mantissa[3,11,19])
2090c6db450SPer-Daniel Olsson 	 */
2100c6db450SPer-Daniel Olsson 	crc = (hweight32(mantissa) + hweight32(exp) + hweight32(count)) % 2;
2110c6db450SPer-Daniel Olsson 	crc |= ((hweight32(mantissa & 0xAAAAA) + hweight32(exp & 0xA)
2120c6db450SPer-Daniel Olsson 		 + hweight32(count & 0xA)) % 2) << 1;
2130c6db450SPer-Daniel Olsson 	crc |= ((hweight32(mantissa & 0x88888) + hweight32(exp & 0x8)
2140c6db450SPer-Daniel Olsson 		 + hweight32(count & 0x8)) % 2) << 2;
2150c6db450SPer-Daniel Olsson 	crc |= (hweight32(mantissa & 0x80808) % 2) << 3;
2160c6db450SPer-Daniel Olsson 
2170c6db450SPer-Daniel Olsson 	return crc;
2180c6db450SPer-Daniel Olsson }
2190c6db450SPer-Daniel Olsson 
opt4060_set_int_state(struct opt4060_chip * chip,u32 state)2200c6db450SPer-Daniel Olsson static int opt4060_set_int_state(struct opt4060_chip *chip, u32 state)
2210c6db450SPer-Daniel Olsson {
2220c6db450SPer-Daniel Olsson 	int ret;
2230c6db450SPer-Daniel Olsson 	unsigned int regval;
2240c6db450SPer-Daniel Olsson 
2250c6db450SPer-Daniel Olsson 	guard(mutex)(&chip->irq_setting_lock);
2260c6db450SPer-Daniel Olsson 
2270c6db450SPer-Daniel Olsson 	regval = FIELD_PREP(OPT4060_INT_CTRL_INT_CFG, state);
2280c6db450SPer-Daniel Olsson 	ret = regmap_update_bits(chip->regmap, OPT4060_INT_CTRL,
2290c6db450SPer-Daniel Olsson 				 OPT4060_INT_CTRL_INT_CFG, regval);
2300c6db450SPer-Daniel Olsson 	if (ret)
2310c6db450SPer-Daniel Olsson 		dev_err(chip->dev, "Failed to set interrupt config\n");
2320c6db450SPer-Daniel Olsson 	return ret;
2330c6db450SPer-Daniel Olsson }
2340c6db450SPer-Daniel Olsson 
opt4060_set_sampling_mode(struct opt4060_chip * chip,bool continuous)2350c6db450SPer-Daniel Olsson static int opt4060_set_sampling_mode(struct opt4060_chip *chip,
2360c6db450SPer-Daniel Olsson 				     bool continuous)
2370c6db450SPer-Daniel Olsson {
2380c6db450SPer-Daniel Olsson 	unsigned int reg;
2390c6db450SPer-Daniel Olsson 	int ret;
2400c6db450SPer-Daniel Olsson 
2410c6db450SPer-Daniel Olsson 	ret = regmap_read(chip->regmap, OPT4060_CTRL, &reg);
2420c6db450SPer-Daniel Olsson 	if (ret < 0) {
2430c6db450SPer-Daniel Olsson 		dev_err(chip->dev, "Failed to read ctrl register\n");
2440c6db450SPer-Daniel Olsson 		return ret;
2450c6db450SPer-Daniel Olsson 	}
2460c6db450SPer-Daniel Olsson 	reg &= ~OPT4060_CTRL_OPER_MODE_MASK;
2470c6db450SPer-Daniel Olsson 	if (continuous)
2480c6db450SPer-Daniel Olsson 		reg |= FIELD_PREP(OPT4060_CTRL_OPER_MODE_MASK,
2490c6db450SPer-Daniel Olsson 				  OPT4060_CTRL_OPER_MODE_CONTINUOUS);
2500c6db450SPer-Daniel Olsson 	else
2510c6db450SPer-Daniel Olsson 		reg |= FIELD_PREP(OPT4060_CTRL_OPER_MODE_MASK,
2520c6db450SPer-Daniel Olsson 				  OPT4060_CTRL_OPER_MODE_ONE_SHOT);
2530c6db450SPer-Daniel Olsson 
2540c6db450SPer-Daniel Olsson 	/*
2550c6db450SPer-Daniel Olsson 	 * Trigger a new conversions by writing to CRTL register. It is not
2560c6db450SPer-Daniel Olsson 	 * possible to use regmap_update_bits() since that will only write when
2570c6db450SPer-Daniel Olsson 	 * data is modified.
2580c6db450SPer-Daniel Olsson 	 */
2590c6db450SPer-Daniel Olsson 	ret = regmap_write(chip->regmap, OPT4060_CTRL, reg);
2600c6db450SPer-Daniel Olsson 	if (ret)
2610c6db450SPer-Daniel Olsson 		dev_err(chip->dev, "Failed to set ctrl register\n");
2620c6db450SPer-Daniel Olsson 	return ret;
2630c6db450SPer-Daniel Olsson }
2640c6db450SPer-Daniel Olsson 
opt4060_event_active(struct opt4060_chip * chip)2650c6db450SPer-Daniel Olsson static bool opt4060_event_active(struct opt4060_chip *chip)
2660c6db450SPer-Daniel Olsson {
2670c6db450SPer-Daniel Olsson 	return chip->thresh_event_lo_active || chip->thresh_event_hi_active;
2680c6db450SPer-Daniel Olsson }
2690c6db450SPer-Daniel Olsson 
opt4060_set_state_common(struct opt4060_chip * chip,bool continuous_sampling,bool continuous_irq)2700c6db450SPer-Daniel Olsson static int opt4060_set_state_common(struct opt4060_chip *chip,
2710c6db450SPer-Daniel Olsson 				    bool continuous_sampling,
2720c6db450SPer-Daniel Olsson 				    bool continuous_irq)
2730c6db450SPer-Daniel Olsson {
2740c6db450SPer-Daniel Olsson 	int ret = 0;
2750c6db450SPer-Daniel Olsson 
2760c6db450SPer-Daniel Olsson 	/* It is important to setup irq before sampling to avoid missing samples. */
2770c6db450SPer-Daniel Olsson 	if (continuous_irq)
2780c6db450SPer-Daniel Olsson 		ret = opt4060_set_int_state(chip, OPT4060_INT_CTRL_ALL_CH);
2790c6db450SPer-Daniel Olsson 	else
2800c6db450SPer-Daniel Olsson 		ret = opt4060_set_int_state(chip, OPT4060_INT_CTRL_THRESHOLD);
2810c6db450SPer-Daniel Olsson 	if (ret) {
2820c6db450SPer-Daniel Olsson 		dev_err(chip->dev, "Failed to set irq state.\n");
2830c6db450SPer-Daniel Olsson 		return ret;
2840c6db450SPer-Daniel Olsson 	}
2850c6db450SPer-Daniel Olsson 
2860c6db450SPer-Daniel Olsson 	if (continuous_sampling || opt4060_event_active(chip))
2870c6db450SPer-Daniel Olsson 		ret = opt4060_set_sampling_mode(chip, true);
2880c6db450SPer-Daniel Olsson 	else
2890c6db450SPer-Daniel Olsson 		ret = opt4060_set_sampling_mode(chip, false);
2900c6db450SPer-Daniel Olsson 	if (ret)
2910c6db450SPer-Daniel Olsson 		dev_err(chip->dev, "Failed to set sampling state.\n");
2920c6db450SPer-Daniel Olsson 	return ret;
2930c6db450SPer-Daniel Olsson }
2940c6db450SPer-Daniel Olsson 
2950c6db450SPer-Daniel Olsson /*
2960c6db450SPer-Daniel Olsson  * Function for setting the driver state for sampling and irq. Either direct
2970c6db450SPer-Daniel Olsson  * mode of buffer mode will be claimed during the transition to prevent races
2980c6db450SPer-Daniel Olsson  * between sysfs read, buffer or events.
2990c6db450SPer-Daniel Olsson  */
opt4060_set_driver_state(struct iio_dev * indio_dev,bool continuous_sampling,bool continuous_irq)3000c6db450SPer-Daniel Olsson static int opt4060_set_driver_state(struct iio_dev *indio_dev,
3010c6db450SPer-Daniel Olsson 				    bool continuous_sampling,
3020c6db450SPer-Daniel Olsson 				    bool continuous_irq)
3030c6db450SPer-Daniel Olsson {
3040c6db450SPer-Daniel Olsson 	struct opt4060_chip *chip = iio_priv(indio_dev);
3050c6db450SPer-Daniel Olsson 	int ret = 0;
3060c6db450SPer-Daniel Olsson any_mode_retry:
3070c6db450SPer-Daniel Olsson 	if (iio_device_claim_buffer_mode(indio_dev)) {
3080c6db450SPer-Daniel Olsson 		/*
3090c6db450SPer-Daniel Olsson 		 * This one is a *bit* hacky. If we cannot claim buffer mode,
3100c6db450SPer-Daniel Olsson 		 * then try direct mode so that we make sure things cannot
3110c6db450SPer-Daniel Olsson 		 * concurrently change. And we just keep trying until we get one
3120c6db450SPer-Daniel Olsson 		 * of the modes...
3130c6db450SPer-Daniel Olsson 		 */
314*7e8c0ec1SJonathan Cameron 		if (!iio_device_claim_direct(indio_dev))
3150c6db450SPer-Daniel Olsson 			goto any_mode_retry;
3160c6db450SPer-Daniel Olsson 		/*
3170c6db450SPer-Daniel Olsson 		 * This path means that we managed to claim direct mode. In
3180c6db450SPer-Daniel Olsson 		 * this case the buffer isn't enabled and it's okay to leave
3190c6db450SPer-Daniel Olsson 		 * continuous mode for sampling and/or irq.
3200c6db450SPer-Daniel Olsson 		 */
3210c6db450SPer-Daniel Olsson 		ret = opt4060_set_state_common(chip, continuous_sampling,
3220c6db450SPer-Daniel Olsson 					       continuous_irq);
323*7e8c0ec1SJonathan Cameron 		iio_device_release_direct(indio_dev);
324*7e8c0ec1SJonathan Cameron 		return ret;
3250c6db450SPer-Daniel Olsson 	} else {
3260c6db450SPer-Daniel Olsson 		/*
3270c6db450SPer-Daniel Olsson 		 * This path means that we managed to claim buffer mode. In
3280c6db450SPer-Daniel Olsson 		 * this case the buffer is enabled and irq and sampling must go
3290c6db450SPer-Daniel Olsson 		 * to or remain continuous, but only if the trigger is from this
3300c6db450SPer-Daniel Olsson 		 * device.
3310c6db450SPer-Daniel Olsson 		 */
3320c6db450SPer-Daniel Olsson 		if (!iio_trigger_validate_own_device(indio_dev->trig, indio_dev))
3330c6db450SPer-Daniel Olsson 			ret = opt4060_set_state_common(chip, true, true);
3340c6db450SPer-Daniel Olsson 		else
3350c6db450SPer-Daniel Olsson 			ret = opt4060_set_state_common(chip, continuous_sampling,
3360c6db450SPer-Daniel Olsson 						       continuous_irq);
3370c6db450SPer-Daniel Olsson 		iio_device_release_buffer_mode(indio_dev);
3380c6db450SPer-Daniel Olsson 	}
3390c6db450SPer-Daniel Olsson 	return ret;
3400c6db450SPer-Daniel Olsson }
3410c6db450SPer-Daniel Olsson 
3420c6db450SPer-Daniel Olsson /*
3430c6db450SPer-Daniel Olsson  * This function is called with framework mutex locked.
3440c6db450SPer-Daniel Olsson  */
opt4060_trigger_set_state(struct iio_trigger * trig,bool state)3450c6db450SPer-Daniel Olsson static int opt4060_trigger_set_state(struct iio_trigger *trig, bool state)
3460c6db450SPer-Daniel Olsson {
3470c6db450SPer-Daniel Olsson 	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
3480c6db450SPer-Daniel Olsson 	struct opt4060_chip *chip = iio_priv(indio_dev);
3490c6db450SPer-Daniel Olsson 
3500c6db450SPer-Daniel Olsson 	return opt4060_set_state_common(chip, state, state);
3510c6db450SPer-Daniel Olsson }
3520c6db450SPer-Daniel Olsson 
opt4060_read_raw_value(struct opt4060_chip * chip,unsigned long address,u32 * raw)3530c6db450SPer-Daniel Olsson static int opt4060_read_raw_value(struct opt4060_chip *chip,
3540c6db450SPer-Daniel Olsson 				  unsigned long address, u32 *raw)
3550c6db450SPer-Daniel Olsson {
3560c6db450SPer-Daniel Olsson 	int ret;
3570c6db450SPer-Daniel Olsson 	u16 result[2];
3580c6db450SPer-Daniel Olsson 	u32 mantissa_raw;
3590c6db450SPer-Daniel Olsson 	u16 msb, lsb;
3600c6db450SPer-Daniel Olsson 	u8 exp, count, crc, calc_crc;
3610c6db450SPer-Daniel Olsson 
3620c6db450SPer-Daniel Olsson 	ret = regmap_bulk_read(chip->regmap, address, result, 2);
3630c6db450SPer-Daniel Olsson 	if (ret) {
3640c6db450SPer-Daniel Olsson 		dev_err(chip->dev, "Reading channel data failed\n");
3650c6db450SPer-Daniel Olsson 		return ret;
3660c6db450SPer-Daniel Olsson 	}
3670c6db450SPer-Daniel Olsson 	exp = FIELD_GET(OPT4060_EXPONENT_MASK, result[0]);
3680c6db450SPer-Daniel Olsson 	msb = FIELD_GET(OPT4060_MSB_MASK, result[0]);
3690c6db450SPer-Daniel Olsson 	count = FIELD_GET(OPT4060_COUNTER_MASK, result[1]);
3700c6db450SPer-Daniel Olsson 	crc = FIELD_GET(OPT4060_CRC_MASK, result[1]);
3710c6db450SPer-Daniel Olsson 	lsb = FIELD_GET(OPT4060_LSB_MASK, result[1]);
3720c6db450SPer-Daniel Olsson 	mantissa_raw = (msb << 8) + lsb;
3730c6db450SPer-Daniel Olsson 	calc_crc = opt4060_calculate_crc(exp, mantissa_raw, count);
3740c6db450SPer-Daniel Olsson 	if (calc_crc != crc)
3750c6db450SPer-Daniel Olsson 		return -EIO;
3760c6db450SPer-Daniel Olsson 	*raw = mantissa_raw << exp;
3770c6db450SPer-Daniel Olsson 	return 0;
3780c6db450SPer-Daniel Olsson }
3790c6db450SPer-Daniel Olsson 
opt4060_trigger_new_samples(struct iio_dev * indio_dev)3800c6db450SPer-Daniel Olsson static int opt4060_trigger_new_samples(struct iio_dev *indio_dev)
3810c6db450SPer-Daniel Olsson {
3820c6db450SPer-Daniel Olsson 	struct opt4060_chip *chip = iio_priv(indio_dev);
3830c6db450SPer-Daniel Olsson 	int ret;
3840c6db450SPer-Daniel Olsson 
3850c6db450SPer-Daniel Olsson 	/*
3860c6db450SPer-Daniel Olsson 	 * The conversion time should be 500us startup time plus the integration time
3870c6db450SPer-Daniel Olsson 	 * times the number of channels. An exact timeout isn't critical, it's better
3880c6db450SPer-Daniel Olsson 	 * not to get incorrect errors in the log. Setting the timeout to double the
3890c6db450SPer-Daniel Olsson 	 * theoretical time plus and extra 100ms margin.
3900c6db450SPer-Daniel Olsson 	 */
3910c6db450SPer-Daniel Olsson 	unsigned int timeout_us = (500 + OPT4060_NUM_CHANS *
3920c6db450SPer-Daniel Olsson 				  opt4060_int_time_reg[chip->int_time][0]) * 2 + 100000;
3930c6db450SPer-Daniel Olsson 
3940c6db450SPer-Daniel Olsson 	/* Setting the state in one shot mode with irq on each sample. */
3950c6db450SPer-Daniel Olsson 	ret = opt4060_set_driver_state(indio_dev, false, true);
3960c6db450SPer-Daniel Olsson 	if (ret)
3970c6db450SPer-Daniel Olsson 		return ret;
3980c6db450SPer-Daniel Olsson 
3990c6db450SPer-Daniel Olsson 	if (chip->irq) {
4000c6db450SPer-Daniel Olsson 		guard(mutex)(&chip->irq_setting_lock);
4010c6db450SPer-Daniel Olsson 		reinit_completion(&chip->completion);
4020c6db450SPer-Daniel Olsson 		if (wait_for_completion_timeout(&chip->completion,
4030c6db450SPer-Daniel Olsson 						usecs_to_jiffies(timeout_us)) == 0) {
4040c6db450SPer-Daniel Olsson 			dev_err(chip->dev, "Completion timed out.\n");
4050c6db450SPer-Daniel Olsson 			return -ETIME;
4060c6db450SPer-Daniel Olsson 		}
4070c6db450SPer-Daniel Olsson 	} else {
4080c6db450SPer-Daniel Olsson 		unsigned int ready;
4090c6db450SPer-Daniel Olsson 
4100c6db450SPer-Daniel Olsson 		ret = regmap_read_poll_timeout(chip->regmap, OPT4060_RES_CTRL,
4110c6db450SPer-Daniel Olsson 					       ready, (ready & OPT4060_RES_CTRL_CONV_READY),
4120c6db450SPer-Daniel Olsson 					       1000, timeout_us);
4130c6db450SPer-Daniel Olsson 		if (ret)
4140c6db450SPer-Daniel Olsson 			dev_err(chip->dev, "Conversion ready did not finish within timeout.\n");
4150c6db450SPer-Daniel Olsson 	}
4160c6db450SPer-Daniel Olsson 	/* Setting the state in one shot mode with irq on thresholds. */
4170c6db450SPer-Daniel Olsson 	return opt4060_set_driver_state(indio_dev, false, false);
4180c6db450SPer-Daniel Olsson }
4190c6db450SPer-Daniel Olsson 
opt4060_read_chan_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val)4200c6db450SPer-Daniel Olsson static int opt4060_read_chan_raw(struct iio_dev *indio_dev,
4210c6db450SPer-Daniel Olsson 				 struct iio_chan_spec const *chan, int *val)
4220c6db450SPer-Daniel Olsson {
4230c6db450SPer-Daniel Olsson 	struct opt4060_chip *chip = iio_priv(indio_dev);
4240c6db450SPer-Daniel Olsson 	u32 adc_raw;
4250c6db450SPer-Daniel Olsson 	int ret;
4260c6db450SPer-Daniel Olsson 
4270c6db450SPer-Daniel Olsson 	ret = opt4060_trigger_new_samples(indio_dev);
4280c6db450SPer-Daniel Olsson 	if (ret) {
4290c6db450SPer-Daniel Olsson 		dev_err(chip->dev, "Failed to trigger new samples.\n");
4300c6db450SPer-Daniel Olsson 		return ret;
4310c6db450SPer-Daniel Olsson 	}
4320c6db450SPer-Daniel Olsson 
4330c6db450SPer-Daniel Olsson 	ret = opt4060_read_raw_value(chip, chan->address, &adc_raw);
4340c6db450SPer-Daniel Olsson 	if (ret) {
4350c6db450SPer-Daniel Olsson 		dev_err(chip->dev, "Reading raw channel data failed.\n");
4360c6db450SPer-Daniel Olsson 		return ret;
4370c6db450SPer-Daniel Olsson 	}
4380c6db450SPer-Daniel Olsson 	*val = adc_raw;
4390c6db450SPer-Daniel Olsson 	return IIO_VAL_INT;
4400c6db450SPer-Daniel Olsson }
4410c6db450SPer-Daniel Olsson 
4420c6db450SPer-Daniel Olsson /*
4430c6db450SPer-Daniel Olsson  * Returns the scale values used for red, green and blue. Scales the raw value
4440c6db450SPer-Daniel Olsson  * so that for a particular test light source, typically white, the measurement
4450c6db450SPer-Daniel Olsson  * intensity is the same across different color channels.
4460c6db450SPer-Daniel Olsson  */
opt4060_get_chan_scale(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2)4470c6db450SPer-Daniel Olsson static int opt4060_get_chan_scale(struct iio_dev *indio_dev,
4480c6db450SPer-Daniel Olsson 				  struct iio_chan_spec const *chan,
4490c6db450SPer-Daniel Olsson 				  int *val, int *val2)
4500c6db450SPer-Daniel Olsson {
4510c6db450SPer-Daniel Olsson 	struct opt4060_chip *chip = iio_priv(indio_dev);
4520c6db450SPer-Daniel Olsson 
4530c6db450SPer-Daniel Olsson 	switch (chan->scan_index) {
4540c6db450SPer-Daniel Olsson 	case OPT4060_RED:
4550c6db450SPer-Daniel Olsson 		/* 2.4 */
4560c6db450SPer-Daniel Olsson 		*val = 2;
4570c6db450SPer-Daniel Olsson 		*val2 = 400000;
4580c6db450SPer-Daniel Olsson 		break;
4590c6db450SPer-Daniel Olsson 	case OPT4060_GREEN:
4600c6db450SPer-Daniel Olsson 		/* 1.0 */
4610c6db450SPer-Daniel Olsson 		*val = 1;
4620c6db450SPer-Daniel Olsson 		*val2 = 0;
4630c6db450SPer-Daniel Olsson 		break;
4640c6db450SPer-Daniel Olsson 	case OPT4060_BLUE:
4650c6db450SPer-Daniel Olsson 		/* 1.3 */
4660c6db450SPer-Daniel Olsson 		*val = 1;
4670c6db450SPer-Daniel Olsson 		*val2 = 300000;
4680c6db450SPer-Daniel Olsson 		break;
4690c6db450SPer-Daniel Olsson 	default:
4700c6db450SPer-Daniel Olsson 		dev_err(chip->dev, "Unexpected channel index.\n");
4710c6db450SPer-Daniel Olsson 		return -EINVAL;
4720c6db450SPer-Daniel Olsson 	}
4730c6db450SPer-Daniel Olsson 	return IIO_VAL_INT_PLUS_MICRO;
4740c6db450SPer-Daniel Olsson }
4750c6db450SPer-Daniel Olsson 
opt4060_calc_illuminance(struct opt4060_chip * chip,int * val)4760c6db450SPer-Daniel Olsson static int opt4060_calc_illuminance(struct opt4060_chip *chip, int *val)
4770c6db450SPer-Daniel Olsson {
4780c6db450SPer-Daniel Olsson 	u32 lux_raw;
4790c6db450SPer-Daniel Olsson 	int ret;
4800c6db450SPer-Daniel Olsson 
4810c6db450SPer-Daniel Olsson 	/* The green wide spectral channel is used for illuminance. */
4820c6db450SPer-Daniel Olsson 	ret = opt4060_read_raw_value(chip, OPT4060_GREEN_MSB, &lux_raw);
4830c6db450SPer-Daniel Olsson 	if (ret) {
4840c6db450SPer-Daniel Olsson 		dev_err(chip->dev, "Reading raw channel data failed\n");
4850c6db450SPer-Daniel Olsson 		return ret;
4860c6db450SPer-Daniel Olsson 	}
4870c6db450SPer-Daniel Olsson 
4880c6db450SPer-Daniel Olsson 	/* Illuminance is calculated by ADC_RAW * 2.15e-3. */
4890c6db450SPer-Daniel Olsson 	*val = DIV_U64_ROUND_CLOSEST((u64)(lux_raw * 215), 1000);
4900c6db450SPer-Daniel Olsson 	return ret;
4910c6db450SPer-Daniel Olsson }
4920c6db450SPer-Daniel Olsson 
opt4060_read_illuminance(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val)4930c6db450SPer-Daniel Olsson static int opt4060_read_illuminance(struct iio_dev *indio_dev,
4940c6db450SPer-Daniel Olsson 				    struct iio_chan_spec const *chan,
4950c6db450SPer-Daniel Olsson 				    int *val)
4960c6db450SPer-Daniel Olsson {
4970c6db450SPer-Daniel Olsson 	struct opt4060_chip *chip = iio_priv(indio_dev);
4980c6db450SPer-Daniel Olsson 	int ret;
4990c6db450SPer-Daniel Olsson 
5000c6db450SPer-Daniel Olsson 	ret = opt4060_trigger_new_samples(indio_dev);
5010c6db450SPer-Daniel Olsson 	if (ret) {
5020c6db450SPer-Daniel Olsson 		dev_err(chip->dev, "Failed to trigger new samples.\n");
5030c6db450SPer-Daniel Olsson 		return ret;
5040c6db450SPer-Daniel Olsson 	}
5050c6db450SPer-Daniel Olsson 	ret = opt4060_calc_illuminance(chip, val);
5060c6db450SPer-Daniel Olsson 	if (ret) {
5070c6db450SPer-Daniel Olsson 		dev_err(chip->dev, "Failed to calculate illuminance.\n");
5080c6db450SPer-Daniel Olsson 		return ret;
5090c6db450SPer-Daniel Olsson 	}
5100c6db450SPer-Daniel Olsson 
5110c6db450SPer-Daniel Olsson 	return IIO_VAL_INT;
5120c6db450SPer-Daniel Olsson }
5130c6db450SPer-Daniel Olsson 
opt4060_set_int_time(struct opt4060_chip * chip)5140c6db450SPer-Daniel Olsson static int opt4060_set_int_time(struct opt4060_chip *chip)
5150c6db450SPer-Daniel Olsson {
5160c6db450SPer-Daniel Olsson 	unsigned int regval;
5170c6db450SPer-Daniel Olsson 	int ret;
5180c6db450SPer-Daniel Olsson 
5190c6db450SPer-Daniel Olsson 	regval = FIELD_PREP(OPT4060_CTRL_CONV_TIME_MASK, chip->int_time);
5200c6db450SPer-Daniel Olsson 	ret = regmap_update_bits(chip->regmap, OPT4060_CTRL,
5210c6db450SPer-Daniel Olsson 				 OPT4060_CTRL_CONV_TIME_MASK, regval);
5220c6db450SPer-Daniel Olsson 	if (ret)
5230c6db450SPer-Daniel Olsson 		dev_err(chip->dev, "Failed to set integration time.\n");
5240c6db450SPer-Daniel Olsson 
5250c6db450SPer-Daniel Olsson 	return ret;
5260c6db450SPer-Daniel Olsson }
5270c6db450SPer-Daniel Olsson 
opt4060_power_down(struct opt4060_chip * chip)5280c6db450SPer-Daniel Olsson static int opt4060_power_down(struct opt4060_chip *chip)
5290c6db450SPer-Daniel Olsson {
5300c6db450SPer-Daniel Olsson 	int ret;
5310c6db450SPer-Daniel Olsson 
5320c6db450SPer-Daniel Olsson 	ret = regmap_clear_bits(chip->regmap, OPT4060_CTRL, OPT4060_CTRL_OPER_MODE_MASK);
5330c6db450SPer-Daniel Olsson 	if (ret)
5340c6db450SPer-Daniel Olsson 		dev_err(chip->dev, "Failed to power down\n");
5350c6db450SPer-Daniel Olsson 
5360c6db450SPer-Daniel Olsson 	return ret;
5370c6db450SPer-Daniel Olsson }
5380c6db450SPer-Daniel Olsson 
opt4060_chip_off_action(void * chip)5390c6db450SPer-Daniel Olsson static void opt4060_chip_off_action(void *chip)
5400c6db450SPer-Daniel Olsson {
5410c6db450SPer-Daniel Olsson 	opt4060_power_down(chip);
5420c6db450SPer-Daniel Olsson }
5430c6db450SPer-Daniel Olsson 
5440c6db450SPer-Daniel Olsson #define _OPT4060_COLOR_CHANNEL(_color, _mask, _ev_spec, _num_ev_spec)		\
5450c6db450SPer-Daniel Olsson {										\
5460c6db450SPer-Daniel Olsson 	.type = IIO_INTENSITY,							\
5470c6db450SPer-Daniel Olsson 	.modified = 1,								\
5480c6db450SPer-Daniel Olsson 	.channel2 = IIO_MOD_LIGHT_##_color,					\
5490c6db450SPer-Daniel Olsson 	.info_mask_separate = _mask,						\
5500c6db450SPer-Daniel Olsson 	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),			\
5510c6db450SPer-Daniel Olsson 	.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),	\
5520c6db450SPer-Daniel Olsson 	.address = OPT4060_##_color##_MSB,					\
5530c6db450SPer-Daniel Olsson 	.scan_index = OPT4060_##_color,						\
5540c6db450SPer-Daniel Olsson 	.scan_type = {								\
5550c6db450SPer-Daniel Olsson 		.sign = 'u',							\
5560c6db450SPer-Daniel Olsson 		.realbits = 32,							\
5570c6db450SPer-Daniel Olsson 		.storagebits = 32,						\
5580c6db450SPer-Daniel Olsson 		.endianness = IIO_CPU,						\
5590c6db450SPer-Daniel Olsson 	},									\
5600c6db450SPer-Daniel Olsson 	.event_spec = _ev_spec,							\
5610c6db450SPer-Daniel Olsson 	.num_event_specs = _num_ev_spec,					\
5620c6db450SPer-Daniel Olsson }
5630c6db450SPer-Daniel Olsson 
5640c6db450SPer-Daniel Olsson #define OPT4060_COLOR_CHANNEL(_color, _mask)					\
5650c6db450SPer-Daniel Olsson 	_OPT4060_COLOR_CHANNEL(_color, _mask, opt4060_event_spec,		\
5660c6db450SPer-Daniel Olsson 			       ARRAY_SIZE(opt4060_event_spec))			\
5670c6db450SPer-Daniel Olsson 
5680c6db450SPer-Daniel Olsson #define OPT4060_COLOR_CHANNEL_NO_EVENTS(_color, _mask)				\
5690c6db450SPer-Daniel Olsson 	_OPT4060_COLOR_CHANNEL(_color, _mask, NULL, 0)				\
5700c6db450SPer-Daniel Olsson 
5710c6db450SPer-Daniel Olsson #define OPT4060_LIGHT_CHANNEL(_channel)						\
5720c6db450SPer-Daniel Olsson {										\
5730c6db450SPer-Daniel Olsson 	.type = IIO_LIGHT,							\
5740c6db450SPer-Daniel Olsson 	.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),			\
5750c6db450SPer-Daniel Olsson 	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),			\
5760c6db450SPer-Daniel Olsson 	.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),	\
5770c6db450SPer-Daniel Olsson 	.scan_index = OPT4060_##_channel,					\
5780c6db450SPer-Daniel Olsson 	.scan_type = {								\
5790c6db450SPer-Daniel Olsson 		.sign = 'u',							\
5800c6db450SPer-Daniel Olsson 		.realbits = 32,							\
5810c6db450SPer-Daniel Olsson 		.storagebits = 32,						\
5820c6db450SPer-Daniel Olsson 		.endianness = IIO_CPU,						\
5830c6db450SPer-Daniel Olsson 	},									\
5840c6db450SPer-Daniel Olsson }
5850c6db450SPer-Daniel Olsson 
5860c6db450SPer-Daniel Olsson static const struct iio_event_spec opt4060_event_spec[] = {
5870c6db450SPer-Daniel Olsson 	{
5880c6db450SPer-Daniel Olsson 		.type = IIO_EV_TYPE_THRESH,
5890c6db450SPer-Daniel Olsson 		.dir = IIO_EV_DIR_RISING,
5900c6db450SPer-Daniel Olsson 		.mask_separate = BIT(IIO_EV_INFO_VALUE) |
5910c6db450SPer-Daniel Olsson 				 BIT(IIO_EV_INFO_ENABLE),
5920c6db450SPer-Daniel Olsson 	}, {
5930c6db450SPer-Daniel Olsson 		.type = IIO_EV_TYPE_THRESH,
5940c6db450SPer-Daniel Olsson 		.dir = IIO_EV_DIR_FALLING,
5950c6db450SPer-Daniel Olsson 		.mask_separate = BIT(IIO_EV_INFO_VALUE) |
5960c6db450SPer-Daniel Olsson 				 BIT(IIO_EV_INFO_ENABLE),
5970c6db450SPer-Daniel Olsson 	}, {
5980c6db450SPer-Daniel Olsson 		.type = IIO_EV_TYPE_THRESH,
5990c6db450SPer-Daniel Olsson 		.dir = IIO_EV_DIR_EITHER,
6000c6db450SPer-Daniel Olsson 		.mask_separate = BIT(IIO_EV_INFO_PERIOD),
6010c6db450SPer-Daniel Olsson 	},
6020c6db450SPer-Daniel Olsson };
6030c6db450SPer-Daniel Olsson 
6040c6db450SPer-Daniel Olsson static const struct iio_chan_spec opt4060_channels[] = {
6050c6db450SPer-Daniel Olsson 	OPT4060_COLOR_CHANNEL(RED, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)),
6060c6db450SPer-Daniel Olsson 	OPT4060_COLOR_CHANNEL(GREEN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)),
6070c6db450SPer-Daniel Olsson 	OPT4060_COLOR_CHANNEL(BLUE, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)),
6080c6db450SPer-Daniel Olsson 	OPT4060_COLOR_CHANNEL(CLEAR, BIT(IIO_CHAN_INFO_RAW)),
6090c6db450SPer-Daniel Olsson 	OPT4060_LIGHT_CHANNEL(ILLUM),
6100c6db450SPer-Daniel Olsson 	IIO_CHAN_SOFT_TIMESTAMP(OPT4060_NUM_CHANS),
6110c6db450SPer-Daniel Olsson };
6120c6db450SPer-Daniel Olsson 
6130c6db450SPer-Daniel Olsson static const struct iio_chan_spec opt4060_channels_no_events[] = {
6140c6db450SPer-Daniel Olsson 	OPT4060_COLOR_CHANNEL_NO_EVENTS(RED, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)),
6150c6db450SPer-Daniel Olsson 	OPT4060_COLOR_CHANNEL_NO_EVENTS(GREEN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)),
6160c6db450SPer-Daniel Olsson 	OPT4060_COLOR_CHANNEL_NO_EVENTS(BLUE, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)),
6170c6db450SPer-Daniel Olsson 	OPT4060_COLOR_CHANNEL_NO_EVENTS(CLEAR, BIT(IIO_CHAN_INFO_RAW)),
6180c6db450SPer-Daniel Olsson 	OPT4060_LIGHT_CHANNEL(ILLUM),
6190c6db450SPer-Daniel Olsson 	IIO_CHAN_SOFT_TIMESTAMP(OPT4060_NUM_CHANS),
6200c6db450SPer-Daniel Olsson };
6210c6db450SPer-Daniel Olsson 
opt4060_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)6220c6db450SPer-Daniel Olsson static int opt4060_read_raw(struct iio_dev *indio_dev,
6230c6db450SPer-Daniel Olsson 			    struct iio_chan_spec const *chan,
6240c6db450SPer-Daniel Olsson 			    int *val, int *val2, long mask)
6250c6db450SPer-Daniel Olsson {
6260c6db450SPer-Daniel Olsson 	struct opt4060_chip *chip = iio_priv(indio_dev);
6270c6db450SPer-Daniel Olsson 
6280c6db450SPer-Daniel Olsson 	switch (mask) {
6290c6db450SPer-Daniel Olsson 	case IIO_CHAN_INFO_RAW:
6300c6db450SPer-Daniel Olsson 		return opt4060_read_chan_raw(indio_dev, chan, val);
6310c6db450SPer-Daniel Olsson 	case IIO_CHAN_INFO_SCALE:
6320c6db450SPer-Daniel Olsson 		return opt4060_get_chan_scale(indio_dev, chan, val, val2);
6330c6db450SPer-Daniel Olsson 	case IIO_CHAN_INFO_PROCESSED:
6340c6db450SPer-Daniel Olsson 		return opt4060_read_illuminance(indio_dev, chan, val);
6350c6db450SPer-Daniel Olsson 	case IIO_CHAN_INFO_INT_TIME:
6360c6db450SPer-Daniel Olsson 		*val = 0;
6370c6db450SPer-Daniel Olsson 		*val2 = opt4060_int_time_reg[chip->int_time][0];
6380c6db450SPer-Daniel Olsson 		return IIO_VAL_INT_PLUS_MICRO;
6390c6db450SPer-Daniel Olsson 	default:
6400c6db450SPer-Daniel Olsson 		return -EINVAL;
6410c6db450SPer-Daniel Olsson 	}
6420c6db450SPer-Daniel Olsson }
6430c6db450SPer-Daniel Olsson 
opt4060_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)6440c6db450SPer-Daniel Olsson static int opt4060_write_raw(struct iio_dev *indio_dev,
6450c6db450SPer-Daniel Olsson 			     struct iio_chan_spec const *chan,
6460c6db450SPer-Daniel Olsson 			     int val, int val2, long mask)
6470c6db450SPer-Daniel Olsson {
6480c6db450SPer-Daniel Olsson 	struct opt4060_chip *chip = iio_priv(indio_dev);
6490c6db450SPer-Daniel Olsson 	int int_time;
6500c6db450SPer-Daniel Olsson 
6510c6db450SPer-Daniel Olsson 	switch (mask) {
6520c6db450SPer-Daniel Olsson 	case IIO_CHAN_INFO_INT_TIME:
6530c6db450SPer-Daniel Olsson 		int_time = opt4060_als_time_to_index(val2);
6540c6db450SPer-Daniel Olsson 		if (int_time < 0)
6550c6db450SPer-Daniel Olsson 			return int_time;
6560c6db450SPer-Daniel Olsson 		chip->int_time = int_time;
6570c6db450SPer-Daniel Olsson 		return opt4060_set_int_time(chip);
6580c6db450SPer-Daniel Olsson 	default:
6590c6db450SPer-Daniel Olsson 		return -EINVAL;
6600c6db450SPer-Daniel Olsson 	}
6610c6db450SPer-Daniel Olsson }
6620c6db450SPer-Daniel Olsson 
opt4060_write_raw_get_fmt(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,long mask)6630c6db450SPer-Daniel Olsson static int opt4060_write_raw_get_fmt(struct iio_dev *indio_dev,
6640c6db450SPer-Daniel Olsson 				     struct iio_chan_spec const *chan,
6650c6db450SPer-Daniel Olsson 				     long mask)
6660c6db450SPer-Daniel Olsson {
6670c6db450SPer-Daniel Olsson 	switch (mask) {
6680c6db450SPer-Daniel Olsson 	case IIO_CHAN_INFO_INT_TIME:
6690c6db450SPer-Daniel Olsson 		return IIO_VAL_INT_PLUS_MICRO;
6700c6db450SPer-Daniel Olsson 	default:
6710c6db450SPer-Daniel Olsson 		return -EINVAL;
6720c6db450SPer-Daniel Olsson 	}
6730c6db450SPer-Daniel Olsson }
6740c6db450SPer-Daniel Olsson 
opt4060_calc_th_reg(u32 adc_val)6750c6db450SPer-Daniel Olsson static u32 opt4060_calc_th_reg(u32 adc_val)
6760c6db450SPer-Daniel Olsson {
6770c6db450SPer-Daniel Olsson 	u32 th_val, th_exp, bits;
6780c6db450SPer-Daniel Olsson 	/*
6790c6db450SPer-Daniel Olsson 	 * The threshold registers take 4 bits of exponent and 12 bits of data
6800c6db450SPer-Daniel Olsson 	 * ADC = TH_VAL << (8 + TH_EXP)
6810c6db450SPer-Daniel Olsson 	 */
6820c6db450SPer-Daniel Olsson 	bits = fls(adc_val);
6830c6db450SPer-Daniel Olsson 
6840c6db450SPer-Daniel Olsson 	if (bits > 31)
6850c6db450SPer-Daniel Olsson 		th_exp = 11; /* Maximum exponent */
6860c6db450SPer-Daniel Olsson 	else if (bits > 20)
6870c6db450SPer-Daniel Olsson 		th_exp = bits - 20;
6880c6db450SPer-Daniel Olsson 	else
6890c6db450SPer-Daniel Olsson 		th_exp = 0;
6900c6db450SPer-Daniel Olsson 	th_val = (adc_val >> (8 + th_exp)) & 0xfff;
6910c6db450SPer-Daniel Olsson 
6920c6db450SPer-Daniel Olsson 	return (th_exp << 12) + th_val;
6930c6db450SPer-Daniel Olsson }
6940c6db450SPer-Daniel Olsson 
opt4060_calc_val_from_th_reg(u32 th_reg)6950c6db450SPer-Daniel Olsson static u32 opt4060_calc_val_from_th_reg(u32 th_reg)
6960c6db450SPer-Daniel Olsson {
6970c6db450SPer-Daniel Olsson 	/*
6980c6db450SPer-Daniel Olsson 	 * The threshold registers take 4 bits of exponent and 12 bits of data
6990c6db450SPer-Daniel Olsson 	 * ADC = TH_VAL << (8 + TH_EXP)
7000c6db450SPer-Daniel Olsson 	 */
7010c6db450SPer-Daniel Olsson 	u32 th_val, th_exp;
7020c6db450SPer-Daniel Olsson 
7030c6db450SPer-Daniel Olsson 	th_exp = (th_reg >> 12) & 0xf;
7040c6db450SPer-Daniel Olsson 	th_val = th_reg & 0xfff;
7050c6db450SPer-Daniel Olsson 
7060c6db450SPer-Daniel Olsson 	return th_val << (8 + th_exp);
7070c6db450SPer-Daniel Olsson }
7080c6db450SPer-Daniel Olsson 
opt4060_read_available(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,const int ** vals,int * type,int * length,long mask)7090c6db450SPer-Daniel Olsson static int opt4060_read_available(struct iio_dev *indio_dev,
7100c6db450SPer-Daniel Olsson 				  struct iio_chan_spec const *chan,
7110c6db450SPer-Daniel Olsson 				  const int **vals, int *type, int *length,
7120c6db450SPer-Daniel Olsson 				  long mask)
7130c6db450SPer-Daniel Olsson {
7140c6db450SPer-Daniel Olsson 	switch (mask) {
7150c6db450SPer-Daniel Olsson 	case IIO_CHAN_INFO_INT_TIME:
7160c6db450SPer-Daniel Olsson 		*length = ARRAY_SIZE(opt4060_int_time_available) * 2;
7170c6db450SPer-Daniel Olsson 		*vals = (const int *)opt4060_int_time_available;
7180c6db450SPer-Daniel Olsson 		*type = IIO_VAL_INT_PLUS_MICRO;
7190c6db450SPer-Daniel Olsson 		return IIO_AVAIL_LIST;
7200c6db450SPer-Daniel Olsson 
7210c6db450SPer-Daniel Olsson 	default:
7220c6db450SPer-Daniel Olsson 		return -EINVAL;
7230c6db450SPer-Daniel Olsson 	}
7240c6db450SPer-Daniel Olsson }
7250c6db450SPer-Daniel Olsson 
opt4060_read_ev_period(struct opt4060_chip * chip,int * val,int * val2)7260c6db450SPer-Daniel Olsson static ssize_t opt4060_read_ev_period(struct opt4060_chip *chip, int *val,
7270c6db450SPer-Daniel Olsson 				      int *val2)
7280c6db450SPer-Daniel Olsson {
7290c6db450SPer-Daniel Olsson 	int ret, pers, fault_count, int_time;
7300c6db450SPer-Daniel Olsson 	u64 uval;
7310c6db450SPer-Daniel Olsson 
7320c6db450SPer-Daniel Olsson 	int_time = opt4060_int_time_reg[chip->int_time][0];
7330c6db450SPer-Daniel Olsson 
7340c6db450SPer-Daniel Olsson 	ret = regmap_read(chip->regmap, OPT4060_CTRL, &fault_count);
7350c6db450SPer-Daniel Olsson 	if (ret < 0)
7360c6db450SPer-Daniel Olsson 		return ret;
7370c6db450SPer-Daniel Olsson 
7380c6db450SPer-Daniel Olsson 	fault_count = fault_count & OPT4060_CTRL_FAULT_COUNT_MASK;
7390c6db450SPer-Daniel Olsson 	switch (fault_count) {
7400c6db450SPer-Daniel Olsson 	case OPT4060_CTRL_FAULT_COUNT_2:
7410c6db450SPer-Daniel Olsson 		pers = 2;
7420c6db450SPer-Daniel Olsson 		break;
7430c6db450SPer-Daniel Olsson 	case OPT4060_CTRL_FAULT_COUNT_4:
7440c6db450SPer-Daniel Olsson 		pers = 4;
7450c6db450SPer-Daniel Olsson 		break;
7460c6db450SPer-Daniel Olsson 	case OPT4060_CTRL_FAULT_COUNT_8:
7470c6db450SPer-Daniel Olsson 		pers = 8;
7480c6db450SPer-Daniel Olsson 		break;
7490c6db450SPer-Daniel Olsson 
7500c6db450SPer-Daniel Olsson 	default:
7510c6db450SPer-Daniel Olsson 		pers = 1;
7520c6db450SPer-Daniel Olsson 		break;
7530c6db450SPer-Daniel Olsson 	}
7540c6db450SPer-Daniel Olsson 
7550c6db450SPer-Daniel Olsson 	uval = mul_u32_u32(int_time, pers);
7560c6db450SPer-Daniel Olsson 	*val = div_u64_rem(uval, MICRO, val2);
7570c6db450SPer-Daniel Olsson 
7580c6db450SPer-Daniel Olsson 	return IIO_VAL_INT_PLUS_MICRO;
7590c6db450SPer-Daniel Olsson }
7600c6db450SPer-Daniel Olsson 
opt4060_write_ev_period(struct opt4060_chip * chip,int val,int val2)7610c6db450SPer-Daniel Olsson static ssize_t opt4060_write_ev_period(struct opt4060_chip *chip, int val,
7620c6db450SPer-Daniel Olsson 				       int val2)
7630c6db450SPer-Daniel Olsson {
7640c6db450SPer-Daniel Olsson 	u64 uval, int_time;
7650c6db450SPer-Daniel Olsson 	unsigned int regval, fault_count_val;
7660c6db450SPer-Daniel Olsson 
7670c6db450SPer-Daniel Olsson 	uval = mul_u32_u32(val, MICRO) + val2;
7680c6db450SPer-Daniel Olsson 	int_time = opt4060_int_time_reg[chip->int_time][0];
7690c6db450SPer-Daniel Olsson 
7700c6db450SPer-Daniel Olsson 	/* Check if the period is closest to 1, 2, 4 or 8 times integration time.*/
7710c6db450SPer-Daniel Olsson 	if (uval <= int_time)
7720c6db450SPer-Daniel Olsson 		fault_count_val = OPT4060_CTRL_FAULT_COUNT_1;
7730c6db450SPer-Daniel Olsson 	else if (uval <= int_time * 2)
7740c6db450SPer-Daniel Olsson 		fault_count_val = OPT4060_CTRL_FAULT_COUNT_2;
7750c6db450SPer-Daniel Olsson 	else if (uval <= int_time * 4)
7760c6db450SPer-Daniel Olsson 		fault_count_val = OPT4060_CTRL_FAULT_COUNT_4;
7770c6db450SPer-Daniel Olsson 	else
7780c6db450SPer-Daniel Olsson 		fault_count_val = OPT4060_CTRL_FAULT_COUNT_8;
7790c6db450SPer-Daniel Olsson 
7800c6db450SPer-Daniel Olsson 	regval = FIELD_PREP(OPT4060_CTRL_FAULT_COUNT_MASK, fault_count_val);
7810c6db450SPer-Daniel Olsson 	return regmap_update_bits(chip->regmap, OPT4060_CTRL,
7820c6db450SPer-Daniel Olsson 				 OPT4060_CTRL_FAULT_COUNT_MASK, regval);
7830c6db450SPer-Daniel Olsson }
7840c6db450SPer-Daniel Olsson 
opt4060_get_channel_sel(struct opt4060_chip * chip,int * ch_sel)7850c6db450SPer-Daniel Olsson static int opt4060_get_channel_sel(struct opt4060_chip *chip, int *ch_sel)
7860c6db450SPer-Daniel Olsson {
7870c6db450SPer-Daniel Olsson 	int ret;
7880c6db450SPer-Daniel Olsson 	u32 regval;
7890c6db450SPer-Daniel Olsson 
7900c6db450SPer-Daniel Olsson 	ret = regmap_read(chip->regmap, OPT4060_INT_CTRL, &regval);
7910c6db450SPer-Daniel Olsson 	if (ret) {
7920c6db450SPer-Daniel Olsson 		dev_err(chip->dev, "Failed to get channel selection.\n");
7930c6db450SPer-Daniel Olsson 		return ret;
7940c6db450SPer-Daniel Olsson 	}
7950c6db450SPer-Daniel Olsson 	*ch_sel = FIELD_GET(OPT4060_INT_CTRL_THRESH_SEL, regval);
7960c6db450SPer-Daniel Olsson 	return ret;
7970c6db450SPer-Daniel Olsson }
7980c6db450SPer-Daniel Olsson 
opt4060_set_channel_sel(struct opt4060_chip * chip,int ch_sel)7990c6db450SPer-Daniel Olsson static int opt4060_set_channel_sel(struct opt4060_chip *chip, int ch_sel)
8000c6db450SPer-Daniel Olsson {
8010c6db450SPer-Daniel Olsson 	int ret;
8020c6db450SPer-Daniel Olsson 	u32 regval;
8030c6db450SPer-Daniel Olsson 
8040c6db450SPer-Daniel Olsson 	regval = FIELD_PREP(OPT4060_INT_CTRL_THRESH_SEL, ch_sel);
8050c6db450SPer-Daniel Olsson 	ret = regmap_update_bits(chip->regmap, OPT4060_INT_CTRL,
8060c6db450SPer-Daniel Olsson 				 OPT4060_INT_CTRL_THRESH_SEL, regval);
8070c6db450SPer-Daniel Olsson 	if (ret)
8080c6db450SPer-Daniel Olsson 		dev_err(chip->dev, "Failed to set channel selection.\n");
8090c6db450SPer-Daniel Olsson 	return ret;
8100c6db450SPer-Daniel Olsson }
8110c6db450SPer-Daniel Olsson 
opt4060_get_thresholds(struct opt4060_chip * chip,u32 * th_lo,u32 * th_hi)8120c6db450SPer-Daniel Olsson static int opt4060_get_thresholds(struct opt4060_chip *chip, u32 *th_lo, u32 *th_hi)
8130c6db450SPer-Daniel Olsson {
8140c6db450SPer-Daniel Olsson 	int ret;
8150c6db450SPer-Daniel Olsson 	u32 regval;
8160c6db450SPer-Daniel Olsson 
8170c6db450SPer-Daniel Olsson 	ret = regmap_read(chip->regmap, OPT4060_THRESHOLD_LOW, &regval);
8180c6db450SPer-Daniel Olsson 	if (ret) {
8190c6db450SPer-Daniel Olsson 		dev_err(chip->dev, "Failed to read THRESHOLD_LOW.\n");
8200c6db450SPer-Daniel Olsson 		return ret;
8210c6db450SPer-Daniel Olsson 	}
8220c6db450SPer-Daniel Olsson 	*th_lo = opt4060_calc_val_from_th_reg(regval);
8230c6db450SPer-Daniel Olsson 
8240c6db450SPer-Daniel Olsson 	ret = regmap_read(chip->regmap, OPT4060_THRESHOLD_HIGH, &regval);
8250c6db450SPer-Daniel Olsson 	if (ret) {
8260c6db450SPer-Daniel Olsson 		dev_err(chip->dev, "Failed to read THRESHOLD_LOW.\n");
8270c6db450SPer-Daniel Olsson 		return ret;
8280c6db450SPer-Daniel Olsson 	}
8290c6db450SPer-Daniel Olsson 	*th_hi = opt4060_calc_val_from_th_reg(regval);
8300c6db450SPer-Daniel Olsson 
8310c6db450SPer-Daniel Olsson 	return ret;
8320c6db450SPer-Daniel Olsson }
8330c6db450SPer-Daniel Olsson 
opt4060_set_thresholds(struct opt4060_chip * chip,u32 th_lo,u32 th_hi)8340c6db450SPer-Daniel Olsson static int opt4060_set_thresholds(struct opt4060_chip *chip, u32 th_lo, u32 th_hi)
8350c6db450SPer-Daniel Olsson {
8360c6db450SPer-Daniel Olsson 	int ret;
8370c6db450SPer-Daniel Olsson 
8380c6db450SPer-Daniel Olsson 	ret = regmap_write(chip->regmap, OPT4060_THRESHOLD_LOW, opt4060_calc_th_reg(th_lo));
8390c6db450SPer-Daniel Olsson 	if (ret) {
8400c6db450SPer-Daniel Olsson 		dev_err(chip->dev, "Failed to write THRESHOLD_LOW.\n");
8410c6db450SPer-Daniel Olsson 		return ret;
8420c6db450SPer-Daniel Olsson 	}
8430c6db450SPer-Daniel Olsson 
8440c6db450SPer-Daniel Olsson 	ret = regmap_write(chip->regmap, OPT4060_THRESHOLD_HIGH, opt4060_calc_th_reg(th_hi));
8450c6db450SPer-Daniel Olsson 	if (ret)
8460c6db450SPer-Daniel Olsson 		dev_err(chip->dev, "Failed to write THRESHOLD_HIGH.\n");
8470c6db450SPer-Daniel Olsson 
8480c6db450SPer-Daniel Olsson 	return ret;
8490c6db450SPer-Daniel Olsson }
8500c6db450SPer-Daniel Olsson 
opt4060_read_event(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,enum iio_event_type type,enum iio_event_direction dir,enum iio_event_info info,int * val,int * val2)8510c6db450SPer-Daniel Olsson static int opt4060_read_event(struct iio_dev *indio_dev,
8520c6db450SPer-Daniel Olsson 			      const struct iio_chan_spec *chan,
8530c6db450SPer-Daniel Olsson 			      enum iio_event_type type,
8540c6db450SPer-Daniel Olsson 			      enum iio_event_direction dir,
8550c6db450SPer-Daniel Olsson 			      enum iio_event_info info,
8560c6db450SPer-Daniel Olsson 			      int *val, int *val2)
8570c6db450SPer-Daniel Olsson {
8580c6db450SPer-Daniel Olsson 	struct opt4060_chip *chip = iio_priv(indio_dev);
8590c6db450SPer-Daniel Olsson 	u32 th_lo, th_hi;
8600c6db450SPer-Daniel Olsson 	int ret;
8610c6db450SPer-Daniel Olsson 
8620c6db450SPer-Daniel Olsson 	if (chan->type != IIO_INTENSITY)
8630c6db450SPer-Daniel Olsson 		return -EINVAL;
8640c6db450SPer-Daniel Olsson 	if (type != IIO_EV_TYPE_THRESH)
8650c6db450SPer-Daniel Olsson 		return -EINVAL;
8660c6db450SPer-Daniel Olsson 
8670c6db450SPer-Daniel Olsson 	switch (info) {
8680c6db450SPer-Daniel Olsson 	case IIO_EV_INFO_VALUE:
8690c6db450SPer-Daniel Olsson 		ret = opt4060_get_thresholds(chip, &th_lo, &th_hi);
8700c6db450SPer-Daniel Olsson 		if (ret)
8710c6db450SPer-Daniel Olsson 			return ret;
8720c6db450SPer-Daniel Olsson 		if (dir == IIO_EV_DIR_FALLING) {
8730c6db450SPer-Daniel Olsson 			*val = th_lo;
8740c6db450SPer-Daniel Olsson 			ret = IIO_VAL_INT;
8750c6db450SPer-Daniel Olsson 		} else if (dir == IIO_EV_DIR_RISING) {
8760c6db450SPer-Daniel Olsson 			*val = th_hi;
8770c6db450SPer-Daniel Olsson 			ret = IIO_VAL_INT;
8780c6db450SPer-Daniel Olsson 		}
8790c6db450SPer-Daniel Olsson 		return ret;
8800c6db450SPer-Daniel Olsson 	case IIO_EV_INFO_PERIOD:
8810c6db450SPer-Daniel Olsson 		return opt4060_read_ev_period(chip, val, val2);
8820c6db450SPer-Daniel Olsson 	default:
8830c6db450SPer-Daniel Olsson 		return -EINVAL;
8840c6db450SPer-Daniel Olsson 	}
8850c6db450SPer-Daniel Olsson }
8860c6db450SPer-Daniel Olsson 
opt4060_write_event(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,enum iio_event_type type,enum iio_event_direction dir,enum iio_event_info info,int val,int val2)8870c6db450SPer-Daniel Olsson static int opt4060_write_event(struct iio_dev *indio_dev,
8880c6db450SPer-Daniel Olsson 			       const struct iio_chan_spec *chan,
8890c6db450SPer-Daniel Olsson 			       enum iio_event_type type,
8900c6db450SPer-Daniel Olsson 			       enum iio_event_direction dir,
8910c6db450SPer-Daniel Olsson 			       enum iio_event_info info,
8920c6db450SPer-Daniel Olsson 			       int val, int val2)
8930c6db450SPer-Daniel Olsson {
8940c6db450SPer-Daniel Olsson 	struct opt4060_chip *chip = iio_priv(indio_dev);
8950c6db450SPer-Daniel Olsson 	u32 th_lo, th_hi;
8960c6db450SPer-Daniel Olsson 	int ret;
8970c6db450SPer-Daniel Olsson 
8980c6db450SPer-Daniel Olsson 	if (chan->type != IIO_INTENSITY)
8990c6db450SPer-Daniel Olsson 		return -EINVAL;
9000c6db450SPer-Daniel Olsson 	if (type != IIO_EV_TYPE_THRESH)
9010c6db450SPer-Daniel Olsson 		return -EINVAL;
9020c6db450SPer-Daniel Olsson 
9030c6db450SPer-Daniel Olsson 	switch (info) {
9040c6db450SPer-Daniel Olsson 	case IIO_EV_INFO_VALUE:
9050c6db450SPer-Daniel Olsson 		ret = opt4060_get_thresholds(chip, &th_lo, &th_hi);
9060c6db450SPer-Daniel Olsson 		if (ret)
9070c6db450SPer-Daniel Olsson 			return ret;
9080c6db450SPer-Daniel Olsson 		if (dir == IIO_EV_DIR_FALLING)
9090c6db450SPer-Daniel Olsson 			th_lo = val;
9100c6db450SPer-Daniel Olsson 		else if (dir == IIO_EV_DIR_RISING)
9110c6db450SPer-Daniel Olsson 			th_hi = val;
9120c6db450SPer-Daniel Olsson 		return opt4060_set_thresholds(chip, th_lo, th_hi);
9130c6db450SPer-Daniel Olsson 	case IIO_EV_INFO_PERIOD:
9140c6db450SPer-Daniel Olsson 		return opt4060_write_ev_period(chip, val, val2);
9150c6db450SPer-Daniel Olsson 	default:
9160c6db450SPer-Daniel Olsson 		return -EINVAL;
9170c6db450SPer-Daniel Olsson 	}
9180c6db450SPer-Daniel Olsson }
9190c6db450SPer-Daniel Olsson 
opt4060_read_event_config(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,enum iio_event_type type,enum iio_event_direction dir)9200c6db450SPer-Daniel Olsson static int opt4060_read_event_config(struct iio_dev *indio_dev,
9210c6db450SPer-Daniel Olsson 				     const struct iio_chan_spec *chan,
9220c6db450SPer-Daniel Olsson 				     enum iio_event_type type,
9230c6db450SPer-Daniel Olsson 				     enum iio_event_direction dir)
9240c6db450SPer-Daniel Olsson {
9250c6db450SPer-Daniel Olsson 	int ch_sel, ch_idx = chan->scan_index;
9260c6db450SPer-Daniel Olsson 	struct opt4060_chip *chip = iio_priv(indio_dev);
9270c6db450SPer-Daniel Olsson 	int ret;
9280c6db450SPer-Daniel Olsson 
9290c6db450SPer-Daniel Olsson 	if (chan->type != IIO_INTENSITY)
9300c6db450SPer-Daniel Olsson 		return -EINVAL;
9310c6db450SPer-Daniel Olsson 	if (type != IIO_EV_TYPE_THRESH)
9320c6db450SPer-Daniel Olsson 		return -EINVAL;
9330c6db450SPer-Daniel Olsson 
9340c6db450SPer-Daniel Olsson 	ret = opt4060_get_channel_sel(chip, &ch_sel);
9350c6db450SPer-Daniel Olsson 	if (ret)
9360c6db450SPer-Daniel Olsson 		return ret;
9370c6db450SPer-Daniel Olsson 
9380c6db450SPer-Daniel Olsson 	if (((dir == IIO_EV_DIR_FALLING) && chip->thresh_event_lo_active) ||
9390c6db450SPer-Daniel Olsson 	    ((dir == IIO_EV_DIR_RISING) && chip->thresh_event_hi_active))
9400c6db450SPer-Daniel Olsson 		return ch_sel == ch_idx;
9410c6db450SPer-Daniel Olsson 
9420c6db450SPer-Daniel Olsson 	return ret;
9430c6db450SPer-Daniel Olsson }
9440c6db450SPer-Daniel Olsson 
opt4060_write_event_config(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,enum iio_event_type type,enum iio_event_direction dir,bool state)9450c6db450SPer-Daniel Olsson static int opt4060_write_event_config(struct iio_dev *indio_dev,
9460c6db450SPer-Daniel Olsson 				      const struct iio_chan_spec *chan,
9470c6db450SPer-Daniel Olsson 				      enum iio_event_type type,
9480c6db450SPer-Daniel Olsson 				      enum iio_event_direction dir, bool state)
9490c6db450SPer-Daniel Olsson {
9500c6db450SPer-Daniel Olsson 	int ch_sel, ch_idx = chan->scan_index;
9510c6db450SPer-Daniel Olsson 	struct opt4060_chip *chip = iio_priv(indio_dev);
9520c6db450SPer-Daniel Olsson 	int ret;
9530c6db450SPer-Daniel Olsson 
9540c6db450SPer-Daniel Olsson 	guard(mutex)(&chip->event_enabling_lock);
9550c6db450SPer-Daniel Olsson 
9560c6db450SPer-Daniel Olsson 	if (chan->type != IIO_INTENSITY)
9570c6db450SPer-Daniel Olsson 		return -EINVAL;
9580c6db450SPer-Daniel Olsson 	if (type != IIO_EV_TYPE_THRESH)
9590c6db450SPer-Daniel Olsson 		return -EINVAL;
9600c6db450SPer-Daniel Olsson 
9610c6db450SPer-Daniel Olsson 	ret = opt4060_get_channel_sel(chip, &ch_sel);
9620c6db450SPer-Daniel Olsson 	if (ret)
9630c6db450SPer-Daniel Olsson 		return ret;
9640c6db450SPer-Daniel Olsson 
9650c6db450SPer-Daniel Olsson 	if (state) {
9660c6db450SPer-Daniel Olsson 		/* Only one channel can be active at the same time */
9670c6db450SPer-Daniel Olsson 		if ((chip->thresh_event_lo_active || chip->thresh_event_hi_active) &&
9680c6db450SPer-Daniel Olsson 		    (ch_idx != ch_sel))
9690c6db450SPer-Daniel Olsson 			return -EBUSY;
9700c6db450SPer-Daniel Olsson 		if (dir == IIO_EV_DIR_FALLING)
9710c6db450SPer-Daniel Olsson 			chip->thresh_event_lo_active = true;
9720c6db450SPer-Daniel Olsson 		else if (dir == IIO_EV_DIR_RISING)
9730c6db450SPer-Daniel Olsson 			chip->thresh_event_hi_active = true;
9740c6db450SPer-Daniel Olsson 		ret = opt4060_set_channel_sel(chip, ch_idx);
9750c6db450SPer-Daniel Olsson 		if (ret)
9760c6db450SPer-Daniel Olsson 			return ret;
9770c6db450SPer-Daniel Olsson 	} else {
9780c6db450SPer-Daniel Olsson 		if (ch_idx == ch_sel) {
9790c6db450SPer-Daniel Olsson 			if (dir == IIO_EV_DIR_FALLING)
9800c6db450SPer-Daniel Olsson 				chip->thresh_event_lo_active = false;
9810c6db450SPer-Daniel Olsson 			else if (dir == IIO_EV_DIR_RISING)
9820c6db450SPer-Daniel Olsson 				chip->thresh_event_hi_active = false;
9830c6db450SPer-Daniel Olsson 		}
9840c6db450SPer-Daniel Olsson 	}
9850c6db450SPer-Daniel Olsson 
9860c6db450SPer-Daniel Olsson 	return opt4060_set_driver_state(indio_dev,
9870c6db450SPer-Daniel Olsson 					chip->thresh_event_hi_active |
9880c6db450SPer-Daniel Olsson 					chip->thresh_event_lo_active,
9890c6db450SPer-Daniel Olsson 					false);
9900c6db450SPer-Daniel Olsson }
9910c6db450SPer-Daniel Olsson 
9920c6db450SPer-Daniel Olsson static const struct iio_info opt4060_info = {
9930c6db450SPer-Daniel Olsson 	.read_raw = opt4060_read_raw,
9940c6db450SPer-Daniel Olsson 	.write_raw = opt4060_write_raw,
9950c6db450SPer-Daniel Olsson 	.write_raw_get_fmt = opt4060_write_raw_get_fmt,
9960c6db450SPer-Daniel Olsson 	.read_avail = opt4060_read_available,
9970c6db450SPer-Daniel Olsson 	.read_event_value = opt4060_read_event,
9980c6db450SPer-Daniel Olsson 	.write_event_value = opt4060_write_event,
9990c6db450SPer-Daniel Olsson 	.read_event_config = opt4060_read_event_config,
10000c6db450SPer-Daniel Olsson 	.write_event_config = opt4060_write_event_config,
10010c6db450SPer-Daniel Olsson };
10020c6db450SPer-Daniel Olsson 
10030c6db450SPer-Daniel Olsson static const struct iio_info opt4060_info_no_irq = {
10040c6db450SPer-Daniel Olsson 	.read_raw = opt4060_read_raw,
10050c6db450SPer-Daniel Olsson 	.write_raw = opt4060_write_raw,
10060c6db450SPer-Daniel Olsson 	.write_raw_get_fmt = opt4060_write_raw_get_fmt,
10070c6db450SPer-Daniel Olsson 	.read_avail = opt4060_read_available,
10080c6db450SPer-Daniel Olsson };
10090c6db450SPer-Daniel Olsson 
opt4060_load_defaults(struct opt4060_chip * chip)10100c6db450SPer-Daniel Olsson static int opt4060_load_defaults(struct opt4060_chip *chip)
10110c6db450SPer-Daniel Olsson {
10120c6db450SPer-Daniel Olsson 	u16 reg;
10130c6db450SPer-Daniel Olsson 	int ret;
10140c6db450SPer-Daniel Olsson 
10150c6db450SPer-Daniel Olsson 	chip->int_time = OPT4060_DEFAULT_CONVERSION_TIME;
10160c6db450SPer-Daniel Olsson 
10170c6db450SPer-Daniel Olsson 	/* Set initial MIN/MAX thresholds */
10180c6db450SPer-Daniel Olsson 	ret = opt4060_set_thresholds(chip, 0, UINT_MAX);
10190c6db450SPer-Daniel Olsson 	if (ret)
10200c6db450SPer-Daniel Olsson 		return ret;
10210c6db450SPer-Daniel Olsson 
10220c6db450SPer-Daniel Olsson 	/*
10230c6db450SPer-Daniel Olsson 	 * Setting auto-range, latched window for thresholds, one-shot conversion
10240c6db450SPer-Daniel Olsson 	 * and quick wake-up mode as default.
10250c6db450SPer-Daniel Olsson 	 */
10260c6db450SPer-Daniel Olsson 	reg = FIELD_PREP(OPT4060_CTRL_RANGE_MASK,
10270c6db450SPer-Daniel Olsson 			 OPT4060_CTRL_LIGHT_SCALE_AUTO);
10280c6db450SPer-Daniel Olsson 	reg |= FIELD_PREP(OPT4060_CTRL_CONV_TIME_MASK, chip->int_time);
10290c6db450SPer-Daniel Olsson 	reg |= FIELD_PREP(OPT4060_CTRL_OPER_MODE_MASK,
10300c6db450SPer-Daniel Olsson 				OPT4060_CTRL_OPER_MODE_ONE_SHOT);
10310c6db450SPer-Daniel Olsson 	reg |= OPT4060_CTRL_QWAKE_MASK | OPT4060_CTRL_LATCH_MASK;
10320c6db450SPer-Daniel Olsson 
10330c6db450SPer-Daniel Olsson 	ret = regmap_write(chip->regmap, OPT4060_CTRL, reg);
10340c6db450SPer-Daniel Olsson 	if (ret)
10350c6db450SPer-Daniel Olsson 		dev_err(chip->dev, "Failed to set configuration\n");
10360c6db450SPer-Daniel Olsson 
10370c6db450SPer-Daniel Olsson 	return ret;
10380c6db450SPer-Daniel Olsson }
10390c6db450SPer-Daniel Olsson 
opt4060_volatile_reg(struct device * dev,unsigned int reg)10400c6db450SPer-Daniel Olsson static bool opt4060_volatile_reg(struct device *dev, unsigned int reg)
10410c6db450SPer-Daniel Olsson {
10420c6db450SPer-Daniel Olsson 	return reg <= OPT4060_CLEAR_LSB || reg == OPT4060_RES_CTRL;
10430c6db450SPer-Daniel Olsson }
10440c6db450SPer-Daniel Olsson 
opt4060_writable_reg(struct device * dev,unsigned int reg)10450c6db450SPer-Daniel Olsson static bool opt4060_writable_reg(struct device *dev, unsigned int reg)
10460c6db450SPer-Daniel Olsson {
10470c6db450SPer-Daniel Olsson 	return reg >= OPT4060_THRESHOLD_LOW || reg >= OPT4060_INT_CTRL;
10480c6db450SPer-Daniel Olsson }
10490c6db450SPer-Daniel Olsson 
opt4060_readonly_reg(struct device * dev,unsigned int reg)10500c6db450SPer-Daniel Olsson static bool opt4060_readonly_reg(struct device *dev, unsigned int reg)
10510c6db450SPer-Daniel Olsson {
10520c6db450SPer-Daniel Olsson 	return reg == OPT4060_DEVICE_ID;
10530c6db450SPer-Daniel Olsson }
10540c6db450SPer-Daniel Olsson 
opt4060_readable_reg(struct device * dev,unsigned int reg)10550c6db450SPer-Daniel Olsson static bool opt4060_readable_reg(struct device *dev, unsigned int reg)
10560c6db450SPer-Daniel Olsson {
10570c6db450SPer-Daniel Olsson 	/* Volatile, writable and read-only registers are readable. */
10580c6db450SPer-Daniel Olsson 	return opt4060_volatile_reg(dev, reg) || opt4060_writable_reg(dev, reg) ||
10590c6db450SPer-Daniel Olsson 	       opt4060_readonly_reg(dev, reg);
10600c6db450SPer-Daniel Olsson }
10610c6db450SPer-Daniel Olsson 
10620c6db450SPer-Daniel Olsson static const struct regmap_config opt4060_regmap_config = {
10630c6db450SPer-Daniel Olsson 	.name = "opt4060",
10640c6db450SPer-Daniel Olsson 	.reg_bits = 8,
10650c6db450SPer-Daniel Olsson 	.val_bits = 16,
10660c6db450SPer-Daniel Olsson 	.cache_type = REGCACHE_RBTREE,
10670c6db450SPer-Daniel Olsson 	.max_register = OPT4060_DEVICE_ID,
10680c6db450SPer-Daniel Olsson 	.readable_reg = opt4060_readable_reg,
10690c6db450SPer-Daniel Olsson 	.writeable_reg = opt4060_writable_reg,
10700c6db450SPer-Daniel Olsson 	.volatile_reg = opt4060_volatile_reg,
10710c6db450SPer-Daniel Olsson 	.val_format_endian = REGMAP_ENDIAN_BIG,
10720c6db450SPer-Daniel Olsson };
10730c6db450SPer-Daniel Olsson 
10740c6db450SPer-Daniel Olsson static const struct iio_trigger_ops opt4060_trigger_ops = {
10750c6db450SPer-Daniel Olsson 	.set_trigger_state = opt4060_trigger_set_state,
10760c6db450SPer-Daniel Olsson };
10770c6db450SPer-Daniel Olsson 
opt4060_trigger_handler(int irq,void * p)10780c6db450SPer-Daniel Olsson static irqreturn_t opt4060_trigger_handler(int irq, void *p)
10790c6db450SPer-Daniel Olsson {
10800c6db450SPer-Daniel Olsson 	struct iio_poll_func *pf = p;
10810c6db450SPer-Daniel Olsson 	struct iio_dev *idev = pf->indio_dev;
10820c6db450SPer-Daniel Olsson 	struct opt4060_chip *chip = iio_priv(idev);
10830c6db450SPer-Daniel Olsson 	struct  {
10840c6db450SPer-Daniel Olsson 		u32 chan[OPT4060_NUM_CHANS];
10850c6db450SPer-Daniel Olsson 		aligned_s64 ts;
10860c6db450SPer-Daniel Olsson 	} raw;
10870c6db450SPer-Daniel Olsson 	int i = 0;
10880c6db450SPer-Daniel Olsson 	int chan, ret;
10890c6db450SPer-Daniel Olsson 
10900c6db450SPer-Daniel Olsson 	/* If the trigger is not from this driver, a new sample is needed.*/
10910c6db450SPer-Daniel Olsson 	if (iio_trigger_validate_own_device(idev->trig, idev))
10920c6db450SPer-Daniel Olsson 		opt4060_trigger_new_samples(idev);
10930c6db450SPer-Daniel Olsson 
10940c6db450SPer-Daniel Olsson 	memset(&raw, 0, sizeof(raw));
10950c6db450SPer-Daniel Olsson 
10960c6db450SPer-Daniel Olsson 	iio_for_each_active_channel(idev, chan) {
10970c6db450SPer-Daniel Olsson 		if (chan == OPT4060_ILLUM)
10980c6db450SPer-Daniel Olsson 			ret = opt4060_calc_illuminance(chip, &raw.chan[i++]);
10990c6db450SPer-Daniel Olsson 		else
11000c6db450SPer-Daniel Olsson 			ret = opt4060_read_raw_value(chip,
11010c6db450SPer-Daniel Olsson 						     idev->channels[chan].address,
11020c6db450SPer-Daniel Olsson 						     &raw.chan[i++]);
11030c6db450SPer-Daniel Olsson 		if (ret) {
11040c6db450SPer-Daniel Olsson 			dev_err(chip->dev, "Reading channel data failed\n");
11050c6db450SPer-Daniel Olsson 			goto err_read;
11060c6db450SPer-Daniel Olsson 		}
11070c6db450SPer-Daniel Olsson 	}
11080c6db450SPer-Daniel Olsson 
11090c6db450SPer-Daniel Olsson 	iio_push_to_buffers_with_timestamp(idev, &raw, pf->timestamp);
11100c6db450SPer-Daniel Olsson err_read:
11110c6db450SPer-Daniel Olsson 	iio_trigger_notify_done(idev->trig);
11120c6db450SPer-Daniel Olsson 	return IRQ_HANDLED;
11130c6db450SPer-Daniel Olsson }
11140c6db450SPer-Daniel Olsson 
opt4060_irq_thread(int irq,void * private)11150c6db450SPer-Daniel Olsson static irqreturn_t opt4060_irq_thread(int irq, void *private)
11160c6db450SPer-Daniel Olsson {
11170c6db450SPer-Daniel Olsson 	struct iio_dev *idev = private;
11180c6db450SPer-Daniel Olsson 	struct opt4060_chip *chip = iio_priv(idev);
11190c6db450SPer-Daniel Olsson 	int ret, dummy;
11200c6db450SPer-Daniel Olsson 	unsigned int int_res;
11210c6db450SPer-Daniel Olsson 
11220c6db450SPer-Daniel Olsson 	ret = regmap_read(chip->regmap, OPT4060_RES_CTRL, &int_res);
11230c6db450SPer-Daniel Olsson 	if (ret < 0) {
11240c6db450SPer-Daniel Olsson 		dev_err(chip->dev, "Failed to read interrupt reasons.\n");
11250c6db450SPer-Daniel Olsson 		return IRQ_NONE;
11260c6db450SPer-Daniel Olsson 	}
11270c6db450SPer-Daniel Olsson 
11280c6db450SPer-Daniel Olsson 	/* Read OPT4060_CTRL to clear interrupt */
11290c6db450SPer-Daniel Olsson 	ret = regmap_read(chip->regmap, OPT4060_CTRL, &dummy);
11300c6db450SPer-Daniel Olsson 	if (ret < 0) {
11310c6db450SPer-Daniel Olsson 		dev_err(chip->dev, "Failed to clear interrupt\n");
11320c6db450SPer-Daniel Olsson 		return IRQ_NONE;
11330c6db450SPer-Daniel Olsson 	}
11340c6db450SPer-Daniel Olsson 
11350c6db450SPer-Daniel Olsson 	/* Handle events */
11360c6db450SPer-Daniel Olsson 	if (int_res & (OPT4060_RES_CTRL_FLAG_H | OPT4060_RES_CTRL_FLAG_L)) {
11370c6db450SPer-Daniel Olsson 		u64 code;
11380c6db450SPer-Daniel Olsson 		int chan = 0;
11390c6db450SPer-Daniel Olsson 
11400c6db450SPer-Daniel Olsson 		ret = opt4060_get_channel_sel(chip, &chan);
11410c6db450SPer-Daniel Olsson 		if (ret) {
11420c6db450SPer-Daniel Olsson 			dev_err(chip->dev, "Failed to read threshold channel.\n");
11430c6db450SPer-Daniel Olsson 			return IRQ_NONE;
11440c6db450SPer-Daniel Olsson 		}
11450c6db450SPer-Daniel Olsson 
11460c6db450SPer-Daniel Olsson 		/* Check if the interrupt is from the lower threshold */
11470c6db450SPer-Daniel Olsson 		if (int_res & OPT4060_RES_CTRL_FLAG_L) {
11480c6db450SPer-Daniel Olsson 			code = IIO_MOD_EVENT_CODE(IIO_INTENSITY,
11490c6db450SPer-Daniel Olsson 						  chan,
11500c6db450SPer-Daniel Olsson 						  idev->channels[chan].channel2,
11510c6db450SPer-Daniel Olsson 						  IIO_EV_TYPE_THRESH,
11520c6db450SPer-Daniel Olsson 						  IIO_EV_DIR_FALLING);
11530c6db450SPer-Daniel Olsson 			iio_push_event(idev, code, iio_get_time_ns(idev));
11540c6db450SPer-Daniel Olsson 		}
11550c6db450SPer-Daniel Olsson 		/* Check if the interrupt is from the upper threshold */
11560c6db450SPer-Daniel Olsson 		if (int_res & OPT4060_RES_CTRL_FLAG_H) {
11570c6db450SPer-Daniel Olsson 			code = IIO_MOD_EVENT_CODE(IIO_INTENSITY,
11580c6db450SPer-Daniel Olsson 						  chan,
11590c6db450SPer-Daniel Olsson 						  idev->channels[chan].channel2,
11600c6db450SPer-Daniel Olsson 						  IIO_EV_TYPE_THRESH,
11610c6db450SPer-Daniel Olsson 						  IIO_EV_DIR_RISING);
11620c6db450SPer-Daniel Olsson 			iio_push_event(idev, code, iio_get_time_ns(idev));
11630c6db450SPer-Daniel Olsson 		}
11640c6db450SPer-Daniel Olsson 	}
11650c6db450SPer-Daniel Olsson 
11660c6db450SPer-Daniel Olsson 	/* Handle conversion ready */
11670c6db450SPer-Daniel Olsson 	if (int_res & OPT4060_RES_CTRL_CONV_READY) {
11680c6db450SPer-Daniel Olsson 		/* Signal completion for potentially waiting reads */
11690c6db450SPer-Daniel Olsson 		complete(&chip->completion);
11700c6db450SPer-Daniel Olsson 
11710c6db450SPer-Daniel Olsson 		/* Handle data ready triggers */
11720c6db450SPer-Daniel Olsson 		if (iio_buffer_enabled(idev))
11730c6db450SPer-Daniel Olsson 			iio_trigger_poll_nested(chip->trig);
11740c6db450SPer-Daniel Olsson 	}
11750c6db450SPer-Daniel Olsson 	return IRQ_HANDLED;
11760c6db450SPer-Daniel Olsson }
11770c6db450SPer-Daniel Olsson 
opt4060_setup_buffer(struct opt4060_chip * chip,struct iio_dev * idev)11780c6db450SPer-Daniel Olsson static int opt4060_setup_buffer(struct opt4060_chip *chip, struct iio_dev *idev)
11790c6db450SPer-Daniel Olsson {
11800c6db450SPer-Daniel Olsson 	int ret;
11810c6db450SPer-Daniel Olsson 
11820c6db450SPer-Daniel Olsson 	ret = devm_iio_triggered_buffer_setup(chip->dev, idev,
11830c6db450SPer-Daniel Olsson 					      &iio_pollfunc_store_time,
11840c6db450SPer-Daniel Olsson 					      opt4060_trigger_handler, NULL);
11850c6db450SPer-Daniel Olsson 	if (ret)
11860c6db450SPer-Daniel Olsson 		return dev_err_probe(chip->dev, ret,
11870c6db450SPer-Daniel Olsson 				     "Buffer setup failed.\n");
11880c6db450SPer-Daniel Olsson 	return ret;
11890c6db450SPer-Daniel Olsson }
11900c6db450SPer-Daniel Olsson 
opt4060_setup_trigger(struct opt4060_chip * chip,struct iio_dev * idev)11910c6db450SPer-Daniel Olsson static int opt4060_setup_trigger(struct opt4060_chip *chip, struct iio_dev *idev)
11920c6db450SPer-Daniel Olsson {
11930c6db450SPer-Daniel Olsson 	struct iio_trigger *data_trigger;
11940c6db450SPer-Daniel Olsson 	char *name;
11950c6db450SPer-Daniel Olsson 	int ret;
11960c6db450SPer-Daniel Olsson 
11970c6db450SPer-Daniel Olsson 	data_trigger = devm_iio_trigger_alloc(chip->dev, "%s-data-ready-dev%d",
11980c6db450SPer-Daniel Olsson 					      idev->name, iio_device_id(idev));
11990c6db450SPer-Daniel Olsson 	if (!data_trigger)
12000c6db450SPer-Daniel Olsson 		return -ENOMEM;
12010c6db450SPer-Daniel Olsson 
12020c6db450SPer-Daniel Olsson 	/*
12030c6db450SPer-Daniel Olsson 	 * The data trigger allows for sample capture on each new conversion
12040c6db450SPer-Daniel Olsson 	 * ready interrupt.
12050c6db450SPer-Daniel Olsson 	 */
12060c6db450SPer-Daniel Olsson 	chip->trig = data_trigger;
12070c6db450SPer-Daniel Olsson 	data_trigger->ops = &opt4060_trigger_ops;
12080c6db450SPer-Daniel Olsson 	iio_trigger_set_drvdata(data_trigger, idev);
12090c6db450SPer-Daniel Olsson 	ret = devm_iio_trigger_register(chip->dev, data_trigger);
12100c6db450SPer-Daniel Olsson 	if (ret)
12110c6db450SPer-Daniel Olsson 		return dev_err_probe(chip->dev, ret,
12120c6db450SPer-Daniel Olsson 				     "Data ready trigger registration failed\n");
12130c6db450SPer-Daniel Olsson 
12140c6db450SPer-Daniel Olsson 	name = devm_kasprintf(chip->dev, GFP_KERNEL, "%s-opt4060",
12150c6db450SPer-Daniel Olsson 			      dev_name(chip->dev));
12160c6db450SPer-Daniel Olsson 	if (!name)
12170c6db450SPer-Daniel Olsson 		return dev_err_probe(chip->dev, -ENOMEM, "Failed to alloc chip name\n");
12180c6db450SPer-Daniel Olsson 
12190c6db450SPer-Daniel Olsson 	ret = devm_request_threaded_irq(chip->dev, chip->irq, NULL, opt4060_irq_thread,
12200c6db450SPer-Daniel Olsson 					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
12210c6db450SPer-Daniel Olsson 					name, idev);
12220c6db450SPer-Daniel Olsson 	if (ret)
12230c6db450SPer-Daniel Olsson 		return dev_err_probe(chip->dev, ret, "Could not request IRQ\n");
12240c6db450SPer-Daniel Olsson 
12250c6db450SPer-Daniel Olsson 	init_completion(&chip->completion);
12260c6db450SPer-Daniel Olsson 
12270c6db450SPer-Daniel Olsson 	ret = devm_mutex_init(chip->dev, &chip->irq_setting_lock);
12280c6db450SPer-Daniel Olsson 	if (ret)
12290c6db450SPer-Daniel Olsson 		return ret;
12300c6db450SPer-Daniel Olsson 
12310c6db450SPer-Daniel Olsson 	ret = devm_mutex_init(chip->dev, &chip->event_enabling_lock);
12320c6db450SPer-Daniel Olsson 	if (ret)
12330c6db450SPer-Daniel Olsson 		return ret;
12340c6db450SPer-Daniel Olsson 
12350c6db450SPer-Daniel Olsson 	ret = regmap_write_bits(chip->regmap, OPT4060_INT_CTRL,
12360c6db450SPer-Daniel Olsson 				OPT4060_INT_CTRL_OUTPUT,
12370c6db450SPer-Daniel Olsson 				OPT4060_INT_CTRL_OUTPUT);
12380c6db450SPer-Daniel Olsson 	if (ret)
12390c6db450SPer-Daniel Olsson 		return dev_err_probe(chip->dev, ret,
12400c6db450SPer-Daniel Olsson 				     "Failed to set interrupt as output\n");
12410c6db450SPer-Daniel Olsson 
12420c6db450SPer-Daniel Olsson 	return 0;
12430c6db450SPer-Daniel Olsson }
12440c6db450SPer-Daniel Olsson 
opt4060_probe(struct i2c_client * client)12450c6db450SPer-Daniel Olsson static int opt4060_probe(struct i2c_client *client)
12460c6db450SPer-Daniel Olsson {
12470c6db450SPer-Daniel Olsson 	struct device *dev = &client->dev;
12480c6db450SPer-Daniel Olsson 	struct opt4060_chip *chip;
12490c6db450SPer-Daniel Olsson 	struct iio_dev *indio_dev;
12500c6db450SPer-Daniel Olsson 	int ret;
12510c6db450SPer-Daniel Olsson 	unsigned int regval, dev_id;
12520c6db450SPer-Daniel Olsson 
12530c6db450SPer-Daniel Olsson 	indio_dev = devm_iio_device_alloc(dev, sizeof(*chip));
12540c6db450SPer-Daniel Olsson 	if (!indio_dev)
12550c6db450SPer-Daniel Olsson 		return -ENOMEM;
12560c6db450SPer-Daniel Olsson 
12570c6db450SPer-Daniel Olsson 	chip = iio_priv(indio_dev);
12580c6db450SPer-Daniel Olsson 
12590c6db450SPer-Daniel Olsson 	ret = devm_regulator_get_enable(dev, "vdd");
12600c6db450SPer-Daniel Olsson 	if (ret)
12610c6db450SPer-Daniel Olsson 		return dev_err_probe(dev, ret, "Failed to enable vdd supply\n");
12620c6db450SPer-Daniel Olsson 
12630c6db450SPer-Daniel Olsson 	chip->regmap = devm_regmap_init_i2c(client, &opt4060_regmap_config);
12640c6db450SPer-Daniel Olsson 	if (IS_ERR(chip->regmap))
12650c6db450SPer-Daniel Olsson 		return dev_err_probe(dev, PTR_ERR(chip->regmap),
12660c6db450SPer-Daniel Olsson 				     "regmap initialization failed\n");
12670c6db450SPer-Daniel Olsson 
12680c6db450SPer-Daniel Olsson 	chip->dev = dev;
12690c6db450SPer-Daniel Olsson 	chip->irq = client->irq;
12700c6db450SPer-Daniel Olsson 
12710c6db450SPer-Daniel Olsson 	ret = regmap_reinit_cache(chip->regmap, &opt4060_regmap_config);
12720c6db450SPer-Daniel Olsson 	if (ret)
12730c6db450SPer-Daniel Olsson 		return dev_err_probe(dev, ret,
12740c6db450SPer-Daniel Olsson 				     "failed to reinit regmap cache\n");
12750c6db450SPer-Daniel Olsson 
12760c6db450SPer-Daniel Olsson 	ret = regmap_read(chip->regmap, OPT4060_DEVICE_ID, &regval);
12770c6db450SPer-Daniel Olsson 	if (ret < 0)
12780c6db450SPer-Daniel Olsson 		return dev_err_probe(dev, ret,
12790c6db450SPer-Daniel Olsson 			"Failed to read the device ID register\n");
12800c6db450SPer-Daniel Olsson 
12810c6db450SPer-Daniel Olsson 	dev_id = FIELD_GET(OPT4060_DEVICE_ID_MASK, regval);
12820c6db450SPer-Daniel Olsson 	if (dev_id != OPT4060_DEVICE_ID_VAL)
12830c6db450SPer-Daniel Olsson 		dev_info(dev, "Device ID: %#04x unknown\n", dev_id);
12840c6db450SPer-Daniel Olsson 
12850c6db450SPer-Daniel Olsson 	if (chip->irq) {
12860c6db450SPer-Daniel Olsson 		indio_dev->info = &opt4060_info;
12870c6db450SPer-Daniel Olsson 		indio_dev->channels = opt4060_channels;
12880c6db450SPer-Daniel Olsson 		indio_dev->num_channels = ARRAY_SIZE(opt4060_channels);
12890c6db450SPer-Daniel Olsson 	} else {
12900c6db450SPer-Daniel Olsson 		indio_dev->info = &opt4060_info_no_irq;
12910c6db450SPer-Daniel Olsson 		indio_dev->channels = opt4060_channels_no_events;
12920c6db450SPer-Daniel Olsson 		indio_dev->num_channels = ARRAY_SIZE(opt4060_channels_no_events);
12930c6db450SPer-Daniel Olsson 	}
12940c6db450SPer-Daniel Olsson 	indio_dev->modes = INDIO_DIRECT_MODE;
12950c6db450SPer-Daniel Olsson 	indio_dev->name = "opt4060";
12960c6db450SPer-Daniel Olsson 
12970c6db450SPer-Daniel Olsson 	ret = opt4060_load_defaults(chip);
12980c6db450SPer-Daniel Olsson 	if (ret < 0)
12990c6db450SPer-Daniel Olsson 		return dev_err_probe(dev, ret,
13000c6db450SPer-Daniel Olsson 				     "Failed to set sensor defaults\n");
13010c6db450SPer-Daniel Olsson 
13020c6db450SPer-Daniel Olsson 	ret = devm_add_action_or_reset(dev, opt4060_chip_off_action, chip);
13030c6db450SPer-Daniel Olsson 	if (ret < 0)
13040c6db450SPer-Daniel Olsson 		return dev_err_probe(dev, ret,
13050c6db450SPer-Daniel Olsson 				     "Failed to setup power off action\n");
13060c6db450SPer-Daniel Olsson 
13070c6db450SPer-Daniel Olsson 	ret = opt4060_setup_buffer(chip, indio_dev);
13080c6db450SPer-Daniel Olsson 	if (ret)
13090c6db450SPer-Daniel Olsson 		return ret;
13100c6db450SPer-Daniel Olsson 
13110c6db450SPer-Daniel Olsson 	if (chip->irq) {
13120c6db450SPer-Daniel Olsson 		ret = opt4060_setup_trigger(chip, indio_dev);
13130c6db450SPer-Daniel Olsson 		if (ret)
13140c6db450SPer-Daniel Olsson 			return ret;
13150c6db450SPer-Daniel Olsson 	}
13160c6db450SPer-Daniel Olsson 
13170c6db450SPer-Daniel Olsson 	return devm_iio_device_register(dev, indio_dev);
13180c6db450SPer-Daniel Olsson }
13190c6db450SPer-Daniel Olsson 
13200c6db450SPer-Daniel Olsson static const struct i2c_device_id opt4060_id[] = {
13210c6db450SPer-Daniel Olsson 	{ "opt4060", },
13220c6db450SPer-Daniel Olsson 	{ }
13230c6db450SPer-Daniel Olsson };
13240c6db450SPer-Daniel Olsson MODULE_DEVICE_TABLE(i2c, opt4060_id);
13250c6db450SPer-Daniel Olsson 
13260c6db450SPer-Daniel Olsson static const struct of_device_id opt4060_of_match[] = {
13270c6db450SPer-Daniel Olsson 	{ .compatible = "ti,opt4060" },
13280c6db450SPer-Daniel Olsson 	{ }
13290c6db450SPer-Daniel Olsson };
13300c6db450SPer-Daniel Olsson MODULE_DEVICE_TABLE(of, opt4060_of_match);
13310c6db450SPer-Daniel Olsson 
13320c6db450SPer-Daniel Olsson static struct i2c_driver opt4060_driver = {
13330c6db450SPer-Daniel Olsson 	.driver = {
13340c6db450SPer-Daniel Olsson 		.name = "opt4060",
13350c6db450SPer-Daniel Olsson 		.of_match_table = opt4060_of_match,
13360c6db450SPer-Daniel Olsson 	},
13370c6db450SPer-Daniel Olsson 	.probe = opt4060_probe,
13380c6db450SPer-Daniel Olsson 	.id_table = opt4060_id,
13390c6db450SPer-Daniel Olsson };
13400c6db450SPer-Daniel Olsson module_i2c_driver(opt4060_driver);
13410c6db450SPer-Daniel Olsson 
13420c6db450SPer-Daniel Olsson MODULE_AUTHOR("Per-Daniel Olsson <perdaniel.olsson@axis.com>");
13430c6db450SPer-Daniel Olsson MODULE_DESCRIPTION("Texas Instruments OPT4060 RGBW color sensor driver");
13440c6db450SPer-Daniel Olsson MODULE_LICENSE("GPL");
1345