162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * si1145.c - Support for Silabs SI1132 and SI1141/2/3/5/6/7 combined ambient
462306a36Sopenharmony_ci * light, UV index and proximity sensors
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright 2014-16 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
762306a36Sopenharmony_ci * Copyright 2016 Crestez Dan Leonard <leonard.crestez@intel.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * SI1132 (7-bit I2C slave address 0x60)
1062306a36Sopenharmony_ci * SI1141/2/3 (7-bit I2C slave address 0x5a)
1162306a36Sopenharmony_ci * SI1145/6/6 (7-bit I2C slave address 0x60)
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/i2c.h>
1662306a36Sopenharmony_ci#include <linux/err.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/delay.h>
1962306a36Sopenharmony_ci#include <linux/irq.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <linux/iio/iio.h>
2262306a36Sopenharmony_ci#include <linux/iio/sysfs.h>
2362306a36Sopenharmony_ci#include <linux/iio/trigger.h>
2462306a36Sopenharmony_ci#include <linux/iio/trigger_consumer.h>
2562306a36Sopenharmony_ci#include <linux/iio/triggered_buffer.h>
2662306a36Sopenharmony_ci#include <linux/iio/buffer.h>
2762306a36Sopenharmony_ci#include <linux/util_macros.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define SI1145_REG_PART_ID		0x00
3062306a36Sopenharmony_ci#define SI1145_REG_REV_ID		0x01
3162306a36Sopenharmony_ci#define SI1145_REG_SEQ_ID		0x02
3262306a36Sopenharmony_ci#define SI1145_REG_INT_CFG		0x03
3362306a36Sopenharmony_ci#define SI1145_REG_IRQ_ENABLE		0x04
3462306a36Sopenharmony_ci#define SI1145_REG_IRQ_MODE		0x05
3562306a36Sopenharmony_ci#define SI1145_REG_HW_KEY		0x07
3662306a36Sopenharmony_ci#define SI1145_REG_MEAS_RATE		0x08
3762306a36Sopenharmony_ci#define SI1145_REG_PS_LED21		0x0f
3862306a36Sopenharmony_ci#define SI1145_REG_PS_LED3		0x10
3962306a36Sopenharmony_ci#define SI1145_REG_UCOEF1		0x13
4062306a36Sopenharmony_ci#define SI1145_REG_UCOEF2		0x14
4162306a36Sopenharmony_ci#define SI1145_REG_UCOEF3		0x15
4262306a36Sopenharmony_ci#define SI1145_REG_UCOEF4		0x16
4362306a36Sopenharmony_ci#define SI1145_REG_PARAM_WR		0x17
4462306a36Sopenharmony_ci#define SI1145_REG_COMMAND		0x18
4562306a36Sopenharmony_ci#define SI1145_REG_RESPONSE		0x20
4662306a36Sopenharmony_ci#define SI1145_REG_IRQ_STATUS		0x21
4762306a36Sopenharmony_ci#define SI1145_REG_ALSVIS_DATA		0x22
4862306a36Sopenharmony_ci#define SI1145_REG_ALSIR_DATA		0x24
4962306a36Sopenharmony_ci#define SI1145_REG_PS1_DATA		0x26
5062306a36Sopenharmony_ci#define SI1145_REG_PS2_DATA		0x28
5162306a36Sopenharmony_ci#define SI1145_REG_PS3_DATA		0x2a
5262306a36Sopenharmony_ci#define SI1145_REG_AUX_DATA		0x2c
5362306a36Sopenharmony_ci#define SI1145_REG_PARAM_RD		0x2e
5462306a36Sopenharmony_ci#define SI1145_REG_CHIP_STAT		0x30
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#define SI1145_UCOEF1_DEFAULT		0x7b
5762306a36Sopenharmony_ci#define SI1145_UCOEF2_DEFAULT		0x6b
5862306a36Sopenharmony_ci#define SI1145_UCOEF3_DEFAULT		0x01
5962306a36Sopenharmony_ci#define SI1145_UCOEF4_DEFAULT		0x00
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/* Helper to figure out PS_LED register / shift per channel */
6262306a36Sopenharmony_ci#define SI1145_PS_LED_REG(ch) \
6362306a36Sopenharmony_ci	(((ch) == 2) ? SI1145_REG_PS_LED3 : SI1145_REG_PS_LED21)
6462306a36Sopenharmony_ci#define SI1145_PS_LED_SHIFT(ch) \
6562306a36Sopenharmony_ci	(((ch) == 1) ? 4 : 0)
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/* Parameter offsets */
6862306a36Sopenharmony_ci#define SI1145_PARAM_CHLIST		0x01
6962306a36Sopenharmony_ci#define SI1145_PARAM_PSLED12_SELECT	0x02
7062306a36Sopenharmony_ci#define SI1145_PARAM_PSLED3_SELECT	0x03
7162306a36Sopenharmony_ci#define SI1145_PARAM_PS_ENCODING	0x05
7262306a36Sopenharmony_ci#define SI1145_PARAM_ALS_ENCODING	0x06
7362306a36Sopenharmony_ci#define SI1145_PARAM_PS1_ADC_MUX	0x07
7462306a36Sopenharmony_ci#define SI1145_PARAM_PS2_ADC_MUX	0x08
7562306a36Sopenharmony_ci#define SI1145_PARAM_PS3_ADC_MUX	0x09
7662306a36Sopenharmony_ci#define SI1145_PARAM_PS_ADC_COUNTER	0x0a
7762306a36Sopenharmony_ci#define SI1145_PARAM_PS_ADC_GAIN	0x0b
7862306a36Sopenharmony_ci#define SI1145_PARAM_PS_ADC_MISC	0x0c
7962306a36Sopenharmony_ci#define SI1145_PARAM_ALS_ADC_MUX	0x0d
8062306a36Sopenharmony_ci#define SI1145_PARAM_ALSIR_ADC_MUX	0x0e
8162306a36Sopenharmony_ci#define SI1145_PARAM_AUX_ADC_MUX	0x0f
8262306a36Sopenharmony_ci#define SI1145_PARAM_ALSVIS_ADC_COUNTER	0x10
8362306a36Sopenharmony_ci#define SI1145_PARAM_ALSVIS_ADC_GAIN	0x11
8462306a36Sopenharmony_ci#define SI1145_PARAM_ALSVIS_ADC_MISC	0x12
8562306a36Sopenharmony_ci#define SI1145_PARAM_LED_RECOVERY	0x1c
8662306a36Sopenharmony_ci#define SI1145_PARAM_ALSIR_ADC_COUNTER	0x1d
8762306a36Sopenharmony_ci#define SI1145_PARAM_ALSIR_ADC_GAIN	0x1e
8862306a36Sopenharmony_ci#define SI1145_PARAM_ALSIR_ADC_MISC	0x1f
8962306a36Sopenharmony_ci#define SI1145_PARAM_ADC_OFFSET		0x1a
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/* Channel enable masks for CHLIST parameter */
9262306a36Sopenharmony_ci#define SI1145_CHLIST_EN_PS1		BIT(0)
9362306a36Sopenharmony_ci#define SI1145_CHLIST_EN_PS2		BIT(1)
9462306a36Sopenharmony_ci#define SI1145_CHLIST_EN_PS3		BIT(2)
9562306a36Sopenharmony_ci#define SI1145_CHLIST_EN_ALSVIS		BIT(4)
9662306a36Sopenharmony_ci#define SI1145_CHLIST_EN_ALSIR		BIT(5)
9762306a36Sopenharmony_ci#define SI1145_CHLIST_EN_AUX		BIT(6)
9862306a36Sopenharmony_ci#define SI1145_CHLIST_EN_UV		BIT(7)
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/* Proximity measurement mode for ADC_MISC parameter */
10162306a36Sopenharmony_ci#define SI1145_PS_ADC_MODE_NORMAL	BIT(2)
10262306a36Sopenharmony_ci/* Signal range mask for ADC_MISC parameter */
10362306a36Sopenharmony_ci#define SI1145_ADC_MISC_RANGE		BIT(5)
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/* Commands for REG_COMMAND */
10662306a36Sopenharmony_ci#define SI1145_CMD_NOP			0x00
10762306a36Sopenharmony_ci#define SI1145_CMD_RESET		0x01
10862306a36Sopenharmony_ci#define SI1145_CMD_PS_FORCE		0x05
10962306a36Sopenharmony_ci#define SI1145_CMD_ALS_FORCE		0x06
11062306a36Sopenharmony_ci#define SI1145_CMD_PSALS_FORCE		0x07
11162306a36Sopenharmony_ci#define SI1145_CMD_PS_PAUSE		0x09
11262306a36Sopenharmony_ci#define SI1145_CMD_ALS_PAUSE		0x0a
11362306a36Sopenharmony_ci#define SI1145_CMD_PSALS_PAUSE		0x0b
11462306a36Sopenharmony_ci#define SI1145_CMD_PS_AUTO		0x0d
11562306a36Sopenharmony_ci#define SI1145_CMD_ALS_AUTO		0x0e
11662306a36Sopenharmony_ci#define SI1145_CMD_PSALS_AUTO		0x0f
11762306a36Sopenharmony_ci#define SI1145_CMD_PARAM_QUERY		0x80
11862306a36Sopenharmony_ci#define SI1145_CMD_PARAM_SET		0xa0
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci#define SI1145_RSP_INVALID_SETTING	0x80
12162306a36Sopenharmony_ci#define SI1145_RSP_COUNTER_MASK		0x0F
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci/* Minimum sleep after each command to ensure it's received */
12462306a36Sopenharmony_ci#define SI1145_COMMAND_MINSLEEP_MS	5
12562306a36Sopenharmony_ci/* Return -ETIMEDOUT after this long */
12662306a36Sopenharmony_ci#define SI1145_COMMAND_TIMEOUT_MS	25
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci/* Interrupt configuration masks for INT_CFG register */
12962306a36Sopenharmony_ci#define SI1145_INT_CFG_OE		BIT(0) /* enable interrupt */
13062306a36Sopenharmony_ci#define SI1145_INT_CFG_MODE		BIT(1) /* auto reset interrupt pin */
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci/* Interrupt enable masks for IRQ_ENABLE register */
13362306a36Sopenharmony_ci#define SI1145_MASK_ALL_IE		(BIT(4) | BIT(3) | BIT(2) | BIT(0))
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci#define SI1145_MUX_TEMP			0x65
13662306a36Sopenharmony_ci#define SI1145_MUX_VDD			0x75
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci/* Proximity LED current; see Table 2 in datasheet */
13962306a36Sopenharmony_ci#define SI1145_LED_CURRENT_45mA		0x04
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cienum {
14262306a36Sopenharmony_ci	SI1132,
14362306a36Sopenharmony_ci	SI1141,
14462306a36Sopenharmony_ci	SI1142,
14562306a36Sopenharmony_ci	SI1143,
14662306a36Sopenharmony_ci	SI1145,
14762306a36Sopenharmony_ci	SI1146,
14862306a36Sopenharmony_ci	SI1147,
14962306a36Sopenharmony_ci};
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistruct si1145_part_info {
15262306a36Sopenharmony_ci	u8 part;
15362306a36Sopenharmony_ci	const struct iio_info *iio_info;
15462306a36Sopenharmony_ci	const struct iio_chan_spec *channels;
15562306a36Sopenharmony_ci	unsigned int num_channels;
15662306a36Sopenharmony_ci	unsigned int num_leds;
15762306a36Sopenharmony_ci	bool uncompressed_meas_rate;
15862306a36Sopenharmony_ci};
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci/**
16162306a36Sopenharmony_ci * struct si1145_data - si1145 chip state data
16262306a36Sopenharmony_ci * @client:	I2C client
16362306a36Sopenharmony_ci * @lock:	mutex to protect shared state.
16462306a36Sopenharmony_ci * @cmdlock:	Low-level mutex to protect command execution only
16562306a36Sopenharmony_ci * @rsp_seq:	Next expected response number or -1 if counter reset required
16662306a36Sopenharmony_ci * @scan_mask:	Saved scan mask to avoid duplicate set_chlist
16762306a36Sopenharmony_ci * @autonomous: If automatic measurements are active (for buffer support)
16862306a36Sopenharmony_ci * @part_info:	Part information
16962306a36Sopenharmony_ci * @trig:	Pointer to iio trigger
17062306a36Sopenharmony_ci * @meas_rate:	Value of MEAS_RATE register. Only set in HW in auto mode
17162306a36Sopenharmony_ci * @buffer:	Used to pack data read from sensor.
17262306a36Sopenharmony_ci */
17362306a36Sopenharmony_cistruct si1145_data {
17462306a36Sopenharmony_ci	struct i2c_client *client;
17562306a36Sopenharmony_ci	struct mutex lock;
17662306a36Sopenharmony_ci	struct mutex cmdlock;
17762306a36Sopenharmony_ci	int rsp_seq;
17862306a36Sopenharmony_ci	const struct si1145_part_info *part_info;
17962306a36Sopenharmony_ci	unsigned long scan_mask;
18062306a36Sopenharmony_ci	bool autonomous;
18162306a36Sopenharmony_ci	struct iio_trigger *trig;
18262306a36Sopenharmony_ci	int meas_rate;
18362306a36Sopenharmony_ci	/*
18462306a36Sopenharmony_ci	 * Ensure timestamp will be naturally aligned if present.
18562306a36Sopenharmony_ci	 * Maximum buffer size (may be only partly used if not all
18662306a36Sopenharmony_ci	 * channels are enabled):
18762306a36Sopenharmony_ci	 *   6*2 bytes channels data + 4 bytes alignment +
18862306a36Sopenharmony_ci	 *   8 bytes timestamp
18962306a36Sopenharmony_ci	 */
19062306a36Sopenharmony_ci	u8 buffer[24] __aligned(8);
19162306a36Sopenharmony_ci};
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci/*
19462306a36Sopenharmony_ci * __si1145_command_reset() - Send CMD_NOP and wait for response 0
19562306a36Sopenharmony_ci *
19662306a36Sopenharmony_ci * Does not modify data->rsp_seq
19762306a36Sopenharmony_ci *
19862306a36Sopenharmony_ci * Return: 0 on success and -errno on error.
19962306a36Sopenharmony_ci */
20062306a36Sopenharmony_cistatic int __si1145_command_reset(struct si1145_data *data)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct device *dev = &data->client->dev;
20362306a36Sopenharmony_ci	unsigned long stop_jiffies;
20462306a36Sopenharmony_ci	int ret;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	ret = i2c_smbus_write_byte_data(data->client, SI1145_REG_COMMAND,
20762306a36Sopenharmony_ci						      SI1145_CMD_NOP);
20862306a36Sopenharmony_ci	if (ret < 0)
20962306a36Sopenharmony_ci		return ret;
21062306a36Sopenharmony_ci	msleep(SI1145_COMMAND_MINSLEEP_MS);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	stop_jiffies = jiffies + SI1145_COMMAND_TIMEOUT_MS * HZ / 1000;
21362306a36Sopenharmony_ci	while (true) {
21462306a36Sopenharmony_ci		ret = i2c_smbus_read_byte_data(data->client,
21562306a36Sopenharmony_ci					       SI1145_REG_RESPONSE);
21662306a36Sopenharmony_ci		if (ret <= 0)
21762306a36Sopenharmony_ci			return ret;
21862306a36Sopenharmony_ci		if (time_after(jiffies, stop_jiffies)) {
21962306a36Sopenharmony_ci			dev_warn(dev, "timeout on reset\n");
22062306a36Sopenharmony_ci			return -ETIMEDOUT;
22162306a36Sopenharmony_ci		}
22262306a36Sopenharmony_ci		msleep(SI1145_COMMAND_MINSLEEP_MS);
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci/*
22762306a36Sopenharmony_ci * si1145_command() - Execute a command and poll the response register
22862306a36Sopenharmony_ci *
22962306a36Sopenharmony_ci * All conversion overflows are reported as -EOVERFLOW
23062306a36Sopenharmony_ci * INVALID_SETTING is reported as -EINVAL
23162306a36Sopenharmony_ci * Timeouts are reported as -ETIMEDOUT
23262306a36Sopenharmony_ci *
23362306a36Sopenharmony_ci * Return: 0 on success or -errno on failure
23462306a36Sopenharmony_ci */
23562306a36Sopenharmony_cistatic int si1145_command(struct si1145_data *data, u8 cmd)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	struct device *dev = &data->client->dev;
23862306a36Sopenharmony_ci	unsigned long stop_jiffies;
23962306a36Sopenharmony_ci	int ret;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	mutex_lock(&data->cmdlock);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (data->rsp_seq < 0) {
24462306a36Sopenharmony_ci		ret = __si1145_command_reset(data);
24562306a36Sopenharmony_ci		if (ret < 0) {
24662306a36Sopenharmony_ci			dev_err(dev, "failed to reset command counter, ret=%d\n",
24762306a36Sopenharmony_ci				ret);
24862306a36Sopenharmony_ci			goto out;
24962306a36Sopenharmony_ci		}
25062306a36Sopenharmony_ci		data->rsp_seq = 0;
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	ret = i2c_smbus_write_byte_data(data->client, SI1145_REG_COMMAND, cmd);
25462306a36Sopenharmony_ci	if (ret) {
25562306a36Sopenharmony_ci		dev_warn(dev, "failed to write command, ret=%d\n", ret);
25662306a36Sopenharmony_ci		goto out;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci	/* Sleep a little to ensure the command is received */
25962306a36Sopenharmony_ci	msleep(SI1145_COMMAND_MINSLEEP_MS);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	stop_jiffies = jiffies + SI1145_COMMAND_TIMEOUT_MS * HZ / 1000;
26262306a36Sopenharmony_ci	while (true) {
26362306a36Sopenharmony_ci		ret = i2c_smbus_read_byte_data(data->client,
26462306a36Sopenharmony_ci					       SI1145_REG_RESPONSE);
26562306a36Sopenharmony_ci		if (ret < 0) {
26662306a36Sopenharmony_ci			dev_warn(dev, "failed to read response, ret=%d\n", ret);
26762306a36Sopenharmony_ci			break;
26862306a36Sopenharmony_ci		}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		if ((ret & ~SI1145_RSP_COUNTER_MASK) == 0) {
27162306a36Sopenharmony_ci			if (ret == data->rsp_seq) {
27262306a36Sopenharmony_ci				if (time_after(jiffies, stop_jiffies)) {
27362306a36Sopenharmony_ci					dev_warn(dev, "timeout on command 0x%02x\n",
27462306a36Sopenharmony_ci						 cmd);
27562306a36Sopenharmony_ci					ret = -ETIMEDOUT;
27662306a36Sopenharmony_ci					break;
27762306a36Sopenharmony_ci				}
27862306a36Sopenharmony_ci				msleep(SI1145_COMMAND_MINSLEEP_MS);
27962306a36Sopenharmony_ci				continue;
28062306a36Sopenharmony_ci			}
28162306a36Sopenharmony_ci			if (ret == ((data->rsp_seq + 1) &
28262306a36Sopenharmony_ci				SI1145_RSP_COUNTER_MASK)) {
28362306a36Sopenharmony_ci				data->rsp_seq = ret;
28462306a36Sopenharmony_ci				ret = 0;
28562306a36Sopenharmony_ci				break;
28662306a36Sopenharmony_ci			}
28762306a36Sopenharmony_ci			dev_warn(dev, "unexpected response counter %d instead of %d\n",
28862306a36Sopenharmony_ci				 ret, (data->rsp_seq + 1) &
28962306a36Sopenharmony_ci					SI1145_RSP_COUNTER_MASK);
29062306a36Sopenharmony_ci			ret = -EIO;
29162306a36Sopenharmony_ci		} else {
29262306a36Sopenharmony_ci			if (ret == SI1145_RSP_INVALID_SETTING) {
29362306a36Sopenharmony_ci				dev_warn(dev, "INVALID_SETTING error on command 0x%02x\n",
29462306a36Sopenharmony_ci					 cmd);
29562306a36Sopenharmony_ci				ret = -EINVAL;
29662306a36Sopenharmony_ci			} else {
29762306a36Sopenharmony_ci				/* All overflows are treated identically */
29862306a36Sopenharmony_ci				dev_dbg(dev, "overflow, ret=%d, cmd=0x%02x\n",
29962306a36Sopenharmony_ci					ret, cmd);
30062306a36Sopenharmony_ci				ret = -EOVERFLOW;
30162306a36Sopenharmony_ci			}
30262306a36Sopenharmony_ci		}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci		/* Force a counter reset next time */
30562306a36Sopenharmony_ci		data->rsp_seq = -1;
30662306a36Sopenharmony_ci		break;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ciout:
31062306a36Sopenharmony_ci	mutex_unlock(&data->cmdlock);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	return ret;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic int si1145_param_update(struct si1145_data *data, u8 op, u8 param,
31662306a36Sopenharmony_ci			       u8 value)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	int ret;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	ret = i2c_smbus_write_byte_data(data->client,
32162306a36Sopenharmony_ci		SI1145_REG_PARAM_WR, value);
32262306a36Sopenharmony_ci	if (ret < 0)
32362306a36Sopenharmony_ci		return ret;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	return si1145_command(data, op | (param & 0x1F));
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic int si1145_param_set(struct si1145_data *data, u8 param, u8 value)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	return si1145_param_update(data, SI1145_CMD_PARAM_SET, param, value);
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci/* Set param. Returns negative errno or current value */
33462306a36Sopenharmony_cistatic int si1145_param_query(struct si1145_data *data, u8 param)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	int ret;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	ret = si1145_command(data, SI1145_CMD_PARAM_QUERY | (param & 0x1F));
33962306a36Sopenharmony_ci	if (ret < 0)
34062306a36Sopenharmony_ci		return ret;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	return i2c_smbus_read_byte_data(data->client, SI1145_REG_PARAM_RD);
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci/* Expand 8 bit compressed value to 16 bit, see Silabs AN498 */
34662306a36Sopenharmony_cistatic u16 si1145_uncompress(u8 x)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	u16 result = 0;
34962306a36Sopenharmony_ci	u8 exponent = 0;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	if (x < 8)
35262306a36Sopenharmony_ci		return 0;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	exponent = (x & 0xf0) >> 4;
35562306a36Sopenharmony_ci	result = 0x10 | (x & 0x0f);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if (exponent >= 4)
35862306a36Sopenharmony_ci		return result << (exponent - 4);
35962306a36Sopenharmony_ci	return result >> (4 - exponent);
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci/* Compress 16 bit value to 8 bit, see Silabs AN498 */
36362306a36Sopenharmony_cistatic u8 si1145_compress(u16 x)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	u32 exponent = 0;
36662306a36Sopenharmony_ci	u32 significand = 0;
36762306a36Sopenharmony_ci	u32 tmp = x;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	if (x == 0x0000)
37062306a36Sopenharmony_ci		return 0x00;
37162306a36Sopenharmony_ci	if (x == 0x0001)
37262306a36Sopenharmony_ci		return 0x08;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	while (1) {
37562306a36Sopenharmony_ci		tmp >>= 1;
37662306a36Sopenharmony_ci		exponent += 1;
37762306a36Sopenharmony_ci		if (tmp == 1)
37862306a36Sopenharmony_ci			break;
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	if (exponent < 5) {
38262306a36Sopenharmony_ci		significand = x << (4 - exponent);
38362306a36Sopenharmony_ci		return (exponent << 4) | (significand & 0xF);
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	significand = x >> (exponent - 5);
38762306a36Sopenharmony_ci	if (significand & 1) {
38862306a36Sopenharmony_ci		significand += 2;
38962306a36Sopenharmony_ci		if (significand & 0x0040) {
39062306a36Sopenharmony_ci			exponent += 1;
39162306a36Sopenharmony_ci			significand >>= 1;
39262306a36Sopenharmony_ci		}
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	return (exponent << 4) | ((significand >> 1) & 0xF);
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci/* Write meas_rate in hardware */
39962306a36Sopenharmony_cistatic int si1145_set_meas_rate(struct si1145_data *data, int interval)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	if (data->part_info->uncompressed_meas_rate)
40262306a36Sopenharmony_ci		return i2c_smbus_write_word_data(data->client,
40362306a36Sopenharmony_ci			SI1145_REG_MEAS_RATE, interval);
40462306a36Sopenharmony_ci	else
40562306a36Sopenharmony_ci		return i2c_smbus_write_byte_data(data->client,
40662306a36Sopenharmony_ci			SI1145_REG_MEAS_RATE, interval);
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic int si1145_read_samp_freq(struct si1145_data *data, int *val, int *val2)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	*val = 32000;
41262306a36Sopenharmony_ci	if (data->part_info->uncompressed_meas_rate)
41362306a36Sopenharmony_ci		*val2 = data->meas_rate;
41462306a36Sopenharmony_ci	else
41562306a36Sopenharmony_ci		*val2 = si1145_uncompress(data->meas_rate);
41662306a36Sopenharmony_ci	return IIO_VAL_FRACTIONAL;
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci/* Set the samp freq in driver private data */
42062306a36Sopenharmony_cistatic int si1145_store_samp_freq(struct si1145_data *data, int val)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	int ret = 0;
42362306a36Sopenharmony_ci	int meas_rate;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	if (val <= 0 || val > 32000)
42662306a36Sopenharmony_ci		return -ERANGE;
42762306a36Sopenharmony_ci	meas_rate = 32000 / val;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	mutex_lock(&data->lock);
43062306a36Sopenharmony_ci	if (data->autonomous) {
43162306a36Sopenharmony_ci		ret = si1145_set_meas_rate(data, meas_rate);
43262306a36Sopenharmony_ci		if (ret)
43362306a36Sopenharmony_ci			goto out;
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci	if (data->part_info->uncompressed_meas_rate)
43662306a36Sopenharmony_ci		data->meas_rate = meas_rate;
43762306a36Sopenharmony_ci	else
43862306a36Sopenharmony_ci		data->meas_rate = si1145_compress(meas_rate);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ciout:
44162306a36Sopenharmony_ci	mutex_unlock(&data->lock);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	return ret;
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cistatic irqreturn_t si1145_trigger_handler(int irq, void *private)
44762306a36Sopenharmony_ci{
44862306a36Sopenharmony_ci	struct iio_poll_func *pf = private;
44962306a36Sopenharmony_ci	struct iio_dev *indio_dev = pf->indio_dev;
45062306a36Sopenharmony_ci	struct si1145_data *data = iio_priv(indio_dev);
45162306a36Sopenharmony_ci	int i, j = 0;
45262306a36Sopenharmony_ci	int ret;
45362306a36Sopenharmony_ci	u8 irq_status = 0;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	if (!data->autonomous) {
45662306a36Sopenharmony_ci		ret = si1145_command(data, SI1145_CMD_PSALS_FORCE);
45762306a36Sopenharmony_ci		if (ret < 0 && ret != -EOVERFLOW)
45862306a36Sopenharmony_ci			goto done;
45962306a36Sopenharmony_ci	} else {
46062306a36Sopenharmony_ci		irq_status = ret = i2c_smbus_read_byte_data(data->client,
46162306a36Sopenharmony_ci				SI1145_REG_IRQ_STATUS);
46262306a36Sopenharmony_ci		if (ret < 0)
46362306a36Sopenharmony_ci			goto done;
46462306a36Sopenharmony_ci		if (!(irq_status & SI1145_MASK_ALL_IE))
46562306a36Sopenharmony_ci			goto done;
46662306a36Sopenharmony_ci	}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	for_each_set_bit(i, indio_dev->active_scan_mask,
46962306a36Sopenharmony_ci		indio_dev->masklength) {
47062306a36Sopenharmony_ci		int run = 1;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci		while (i + run < indio_dev->masklength) {
47362306a36Sopenharmony_ci			if (!test_bit(i + run, indio_dev->active_scan_mask))
47462306a36Sopenharmony_ci				break;
47562306a36Sopenharmony_ci			if (indio_dev->channels[i + run].address !=
47662306a36Sopenharmony_ci				indio_dev->channels[i].address + 2 * run)
47762306a36Sopenharmony_ci				break;
47862306a36Sopenharmony_ci			run++;
47962306a36Sopenharmony_ci		}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci		ret = i2c_smbus_read_i2c_block_data_or_emulated(
48262306a36Sopenharmony_ci				data->client, indio_dev->channels[i].address,
48362306a36Sopenharmony_ci				sizeof(u16) * run, &data->buffer[j]);
48462306a36Sopenharmony_ci		if (ret < 0)
48562306a36Sopenharmony_ci			goto done;
48662306a36Sopenharmony_ci		j += run * sizeof(u16);
48762306a36Sopenharmony_ci		i += run - 1;
48862306a36Sopenharmony_ci	}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	if (data->autonomous) {
49162306a36Sopenharmony_ci		ret = i2c_smbus_write_byte_data(data->client,
49262306a36Sopenharmony_ci				SI1145_REG_IRQ_STATUS,
49362306a36Sopenharmony_ci				irq_status & SI1145_MASK_ALL_IE);
49462306a36Sopenharmony_ci		if (ret < 0)
49562306a36Sopenharmony_ci			goto done;
49662306a36Sopenharmony_ci	}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
49962306a36Sopenharmony_ci		iio_get_time_ns(indio_dev));
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_cidone:
50262306a36Sopenharmony_ci	iio_trigger_notify_done(indio_dev->trig);
50362306a36Sopenharmony_ci	return IRQ_HANDLED;
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic int si1145_set_chlist(struct iio_dev *indio_dev, unsigned long scan_mask)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	struct si1145_data *data = iio_priv(indio_dev);
50962306a36Sopenharmony_ci	u8 reg = 0, mux;
51062306a36Sopenharmony_ci	int ret;
51162306a36Sopenharmony_ci	int i;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	/* channel list already set, no need to reprogram */
51462306a36Sopenharmony_ci	if (data->scan_mask == scan_mask)
51562306a36Sopenharmony_ci		return 0;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	for_each_set_bit(i, &scan_mask, indio_dev->masklength) {
51862306a36Sopenharmony_ci		switch (indio_dev->channels[i].address) {
51962306a36Sopenharmony_ci		case SI1145_REG_ALSVIS_DATA:
52062306a36Sopenharmony_ci			reg |= SI1145_CHLIST_EN_ALSVIS;
52162306a36Sopenharmony_ci			break;
52262306a36Sopenharmony_ci		case SI1145_REG_ALSIR_DATA:
52362306a36Sopenharmony_ci			reg |= SI1145_CHLIST_EN_ALSIR;
52462306a36Sopenharmony_ci			break;
52562306a36Sopenharmony_ci		case SI1145_REG_PS1_DATA:
52662306a36Sopenharmony_ci			reg |= SI1145_CHLIST_EN_PS1;
52762306a36Sopenharmony_ci			break;
52862306a36Sopenharmony_ci		case SI1145_REG_PS2_DATA:
52962306a36Sopenharmony_ci			reg |= SI1145_CHLIST_EN_PS2;
53062306a36Sopenharmony_ci			break;
53162306a36Sopenharmony_ci		case SI1145_REG_PS3_DATA:
53262306a36Sopenharmony_ci			reg |= SI1145_CHLIST_EN_PS3;
53362306a36Sopenharmony_ci			break;
53462306a36Sopenharmony_ci		case SI1145_REG_AUX_DATA:
53562306a36Sopenharmony_ci			switch (indio_dev->channels[i].type) {
53662306a36Sopenharmony_ci			case IIO_UVINDEX:
53762306a36Sopenharmony_ci				reg |= SI1145_CHLIST_EN_UV;
53862306a36Sopenharmony_ci				break;
53962306a36Sopenharmony_ci			default:
54062306a36Sopenharmony_ci				reg |= SI1145_CHLIST_EN_AUX;
54162306a36Sopenharmony_ci				if (indio_dev->channels[i].type == IIO_TEMP)
54262306a36Sopenharmony_ci					mux = SI1145_MUX_TEMP;
54362306a36Sopenharmony_ci				else
54462306a36Sopenharmony_ci					mux = SI1145_MUX_VDD;
54562306a36Sopenharmony_ci				ret = si1145_param_set(data,
54662306a36Sopenharmony_ci					SI1145_PARAM_AUX_ADC_MUX, mux);
54762306a36Sopenharmony_ci				if (ret < 0)
54862306a36Sopenharmony_ci					return ret;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci				break;
55162306a36Sopenharmony_ci			}
55262306a36Sopenharmony_ci		}
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	data->scan_mask = scan_mask;
55662306a36Sopenharmony_ci	ret = si1145_param_set(data, SI1145_PARAM_CHLIST, reg);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	return ret < 0 ? ret : 0;
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_cistatic int si1145_measure(struct iio_dev *indio_dev,
56262306a36Sopenharmony_ci			  struct iio_chan_spec const *chan)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	struct si1145_data *data = iio_priv(indio_dev);
56562306a36Sopenharmony_ci	u8 cmd;
56662306a36Sopenharmony_ci	int ret;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	ret = si1145_set_chlist(indio_dev, BIT(chan->scan_index));
56962306a36Sopenharmony_ci	if (ret < 0)
57062306a36Sopenharmony_ci		return ret;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	cmd = (chan->type == IIO_PROXIMITY) ? SI1145_CMD_PS_FORCE :
57362306a36Sopenharmony_ci		SI1145_CMD_ALS_FORCE;
57462306a36Sopenharmony_ci	ret = si1145_command(data, cmd);
57562306a36Sopenharmony_ci	if (ret < 0 && ret != -EOVERFLOW)
57662306a36Sopenharmony_ci		return ret;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	return i2c_smbus_read_word_data(data->client, chan->address);
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci/*
58262306a36Sopenharmony_ci * Conversion between iio scale and ADC_GAIN values
58362306a36Sopenharmony_ci * These could be further adjusted but proximity/intensity are dimensionless
58462306a36Sopenharmony_ci */
58562306a36Sopenharmony_cistatic const int si1145_proximity_scale_available[] = {
58662306a36Sopenharmony_ci	128, 64, 32, 16, 8, 4};
58762306a36Sopenharmony_cistatic const int si1145_intensity_scale_available[] = {
58862306a36Sopenharmony_ci	128, 64, 32, 16, 8, 4, 2, 1};
58962306a36Sopenharmony_cistatic IIO_CONST_ATTR(in_proximity_scale_available,
59062306a36Sopenharmony_ci	"128 64 32 16 8 4");
59162306a36Sopenharmony_cistatic IIO_CONST_ATTR(in_intensity_scale_available,
59262306a36Sopenharmony_ci	"128 64 32 16 8 4 2 1");
59362306a36Sopenharmony_cistatic IIO_CONST_ATTR(in_intensity_ir_scale_available,
59462306a36Sopenharmony_ci	"128 64 32 16 8 4 2 1");
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cistatic int si1145_scale_from_adcgain(int regval)
59762306a36Sopenharmony_ci{
59862306a36Sopenharmony_ci	return 128 >> regval;
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_cistatic int si1145_proximity_adcgain_from_scale(int val, int val2)
60262306a36Sopenharmony_ci{
60362306a36Sopenharmony_ci	val = find_closest_descending(val, si1145_proximity_scale_available,
60462306a36Sopenharmony_ci				ARRAY_SIZE(si1145_proximity_scale_available));
60562306a36Sopenharmony_ci	if (val < 0 || val > 5 || val2 != 0)
60662306a36Sopenharmony_ci		return -EINVAL;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	return val;
60962306a36Sopenharmony_ci}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_cistatic int si1145_intensity_adcgain_from_scale(int val, int val2)
61262306a36Sopenharmony_ci{
61362306a36Sopenharmony_ci	val = find_closest_descending(val, si1145_intensity_scale_available,
61462306a36Sopenharmony_ci				ARRAY_SIZE(si1145_intensity_scale_available));
61562306a36Sopenharmony_ci	if (val < 0 || val > 7 || val2 != 0)
61662306a36Sopenharmony_ci		return -EINVAL;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	return val;
61962306a36Sopenharmony_ci}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_cistatic int si1145_read_raw(struct iio_dev *indio_dev,
62262306a36Sopenharmony_ci				struct iio_chan_spec const *chan,
62362306a36Sopenharmony_ci				int *val, int *val2, long mask)
62462306a36Sopenharmony_ci{
62562306a36Sopenharmony_ci	struct si1145_data *data = iio_priv(indio_dev);
62662306a36Sopenharmony_ci	int ret;
62762306a36Sopenharmony_ci	u8 reg;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	switch (mask) {
63062306a36Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
63162306a36Sopenharmony_ci		switch (chan->type) {
63262306a36Sopenharmony_ci		case IIO_INTENSITY:
63362306a36Sopenharmony_ci		case IIO_PROXIMITY:
63462306a36Sopenharmony_ci		case IIO_VOLTAGE:
63562306a36Sopenharmony_ci		case IIO_TEMP:
63662306a36Sopenharmony_ci		case IIO_UVINDEX:
63762306a36Sopenharmony_ci			ret = iio_device_claim_direct_mode(indio_dev);
63862306a36Sopenharmony_ci			if (ret)
63962306a36Sopenharmony_ci				return ret;
64062306a36Sopenharmony_ci			ret = si1145_measure(indio_dev, chan);
64162306a36Sopenharmony_ci			iio_device_release_direct_mode(indio_dev);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci			if (ret < 0)
64462306a36Sopenharmony_ci				return ret;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci			*val = ret;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci			return IIO_VAL_INT;
64962306a36Sopenharmony_ci		case IIO_CURRENT:
65062306a36Sopenharmony_ci			ret = i2c_smbus_read_byte_data(data->client,
65162306a36Sopenharmony_ci				SI1145_PS_LED_REG(chan->channel));
65262306a36Sopenharmony_ci			if (ret < 0)
65362306a36Sopenharmony_ci				return ret;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci			*val = (ret >> SI1145_PS_LED_SHIFT(chan->channel))
65662306a36Sopenharmony_ci				& 0x0f;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci			return IIO_VAL_INT;
65962306a36Sopenharmony_ci		default:
66062306a36Sopenharmony_ci			return -EINVAL;
66162306a36Sopenharmony_ci		}
66262306a36Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
66362306a36Sopenharmony_ci		switch (chan->type) {
66462306a36Sopenharmony_ci		case IIO_PROXIMITY:
66562306a36Sopenharmony_ci			reg = SI1145_PARAM_PS_ADC_GAIN;
66662306a36Sopenharmony_ci			break;
66762306a36Sopenharmony_ci		case IIO_INTENSITY:
66862306a36Sopenharmony_ci			if (chan->channel2 == IIO_MOD_LIGHT_IR)
66962306a36Sopenharmony_ci				reg = SI1145_PARAM_ALSIR_ADC_GAIN;
67062306a36Sopenharmony_ci			else
67162306a36Sopenharmony_ci				reg = SI1145_PARAM_ALSVIS_ADC_GAIN;
67262306a36Sopenharmony_ci			break;
67362306a36Sopenharmony_ci		case IIO_TEMP:
67462306a36Sopenharmony_ci			*val = 28;
67562306a36Sopenharmony_ci			*val2 = 571429;
67662306a36Sopenharmony_ci			return IIO_VAL_INT_PLUS_MICRO;
67762306a36Sopenharmony_ci		case IIO_UVINDEX:
67862306a36Sopenharmony_ci			*val = 0;
67962306a36Sopenharmony_ci			*val2 = 10000;
68062306a36Sopenharmony_ci			return IIO_VAL_INT_PLUS_MICRO;
68162306a36Sopenharmony_ci		default:
68262306a36Sopenharmony_ci			return -EINVAL;
68362306a36Sopenharmony_ci		}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci		ret = si1145_param_query(data, reg);
68662306a36Sopenharmony_ci		if (ret < 0)
68762306a36Sopenharmony_ci			return ret;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci		*val = si1145_scale_from_adcgain(ret & 0x07);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci		return IIO_VAL_INT;
69262306a36Sopenharmony_ci	case IIO_CHAN_INFO_OFFSET:
69362306a36Sopenharmony_ci		switch (chan->type) {
69462306a36Sopenharmony_ci		case IIO_TEMP:
69562306a36Sopenharmony_ci			/*
69662306a36Sopenharmony_ci			 * -ADC offset - ADC counts @ 25°C -
69762306a36Sopenharmony_ci			 *   35 * ADC counts / °C
69862306a36Sopenharmony_ci			 */
69962306a36Sopenharmony_ci			*val = -256 - 11136 + 25 * 35;
70062306a36Sopenharmony_ci			return IIO_VAL_INT;
70162306a36Sopenharmony_ci		default:
70262306a36Sopenharmony_ci			/*
70362306a36Sopenharmony_ci			 * All ADC measurements have are by default offset
70462306a36Sopenharmony_ci			 * by -256
70562306a36Sopenharmony_ci			 * See AN498 5.6.3
70662306a36Sopenharmony_ci			 */
70762306a36Sopenharmony_ci			ret = si1145_param_query(data, SI1145_PARAM_ADC_OFFSET);
70862306a36Sopenharmony_ci			if (ret < 0)
70962306a36Sopenharmony_ci				return ret;
71062306a36Sopenharmony_ci			*val = -si1145_uncompress(ret);
71162306a36Sopenharmony_ci			return IIO_VAL_INT;
71262306a36Sopenharmony_ci		}
71362306a36Sopenharmony_ci	case IIO_CHAN_INFO_SAMP_FREQ:
71462306a36Sopenharmony_ci		return si1145_read_samp_freq(data, val, val2);
71562306a36Sopenharmony_ci	default:
71662306a36Sopenharmony_ci		return -EINVAL;
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_cistatic int si1145_write_raw(struct iio_dev *indio_dev,
72162306a36Sopenharmony_ci			       struct iio_chan_spec const *chan,
72262306a36Sopenharmony_ci			       int val, int val2, long mask)
72362306a36Sopenharmony_ci{
72462306a36Sopenharmony_ci	struct si1145_data *data = iio_priv(indio_dev);
72562306a36Sopenharmony_ci	u8 reg1, reg2, shift;
72662306a36Sopenharmony_ci	int ret;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	switch (mask) {
72962306a36Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
73062306a36Sopenharmony_ci		switch (chan->type) {
73162306a36Sopenharmony_ci		case IIO_PROXIMITY:
73262306a36Sopenharmony_ci			val = si1145_proximity_adcgain_from_scale(val, val2);
73362306a36Sopenharmony_ci			if (val < 0)
73462306a36Sopenharmony_ci				return val;
73562306a36Sopenharmony_ci			reg1 = SI1145_PARAM_PS_ADC_GAIN;
73662306a36Sopenharmony_ci			reg2 = SI1145_PARAM_PS_ADC_COUNTER;
73762306a36Sopenharmony_ci			break;
73862306a36Sopenharmony_ci		case IIO_INTENSITY:
73962306a36Sopenharmony_ci			val = si1145_intensity_adcgain_from_scale(val, val2);
74062306a36Sopenharmony_ci			if (val < 0)
74162306a36Sopenharmony_ci				return val;
74262306a36Sopenharmony_ci			if (chan->channel2 == IIO_MOD_LIGHT_IR) {
74362306a36Sopenharmony_ci				reg1 = SI1145_PARAM_ALSIR_ADC_GAIN;
74462306a36Sopenharmony_ci				reg2 = SI1145_PARAM_ALSIR_ADC_COUNTER;
74562306a36Sopenharmony_ci			} else {
74662306a36Sopenharmony_ci				reg1 = SI1145_PARAM_ALSVIS_ADC_GAIN;
74762306a36Sopenharmony_ci				reg2 = SI1145_PARAM_ALSVIS_ADC_COUNTER;
74862306a36Sopenharmony_ci			}
74962306a36Sopenharmony_ci			break;
75062306a36Sopenharmony_ci		default:
75162306a36Sopenharmony_ci			return -EINVAL;
75262306a36Sopenharmony_ci		}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci		ret = iio_device_claim_direct_mode(indio_dev);
75562306a36Sopenharmony_ci		if (ret)
75662306a36Sopenharmony_ci			return ret;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci		ret = si1145_param_set(data, reg1, val);
75962306a36Sopenharmony_ci		if (ret < 0) {
76062306a36Sopenharmony_ci			iio_device_release_direct_mode(indio_dev);
76162306a36Sopenharmony_ci			return ret;
76262306a36Sopenharmony_ci		}
76362306a36Sopenharmony_ci		/* Set recovery period to one's complement of gain */
76462306a36Sopenharmony_ci		ret = si1145_param_set(data, reg2, (~val & 0x07) << 4);
76562306a36Sopenharmony_ci		iio_device_release_direct_mode(indio_dev);
76662306a36Sopenharmony_ci		return ret;
76762306a36Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
76862306a36Sopenharmony_ci		if (chan->type != IIO_CURRENT)
76962306a36Sopenharmony_ci			return -EINVAL;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci		if (val < 0 || val > 15 || val2 != 0)
77262306a36Sopenharmony_ci			return -EINVAL;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci		reg1 = SI1145_PS_LED_REG(chan->channel);
77562306a36Sopenharmony_ci		shift = SI1145_PS_LED_SHIFT(chan->channel);
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci		ret = iio_device_claim_direct_mode(indio_dev);
77862306a36Sopenharmony_ci		if (ret)
77962306a36Sopenharmony_ci			return ret;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci		ret = i2c_smbus_read_byte_data(data->client, reg1);
78262306a36Sopenharmony_ci		if (ret < 0) {
78362306a36Sopenharmony_ci			iio_device_release_direct_mode(indio_dev);
78462306a36Sopenharmony_ci			return ret;
78562306a36Sopenharmony_ci		}
78662306a36Sopenharmony_ci		ret = i2c_smbus_write_byte_data(data->client, reg1,
78762306a36Sopenharmony_ci			(ret & ~(0x0f << shift)) |
78862306a36Sopenharmony_ci			((val & 0x0f) << shift));
78962306a36Sopenharmony_ci		iio_device_release_direct_mode(indio_dev);
79062306a36Sopenharmony_ci		return ret;
79162306a36Sopenharmony_ci	case IIO_CHAN_INFO_SAMP_FREQ:
79262306a36Sopenharmony_ci		return si1145_store_samp_freq(data, val);
79362306a36Sopenharmony_ci	default:
79462306a36Sopenharmony_ci		return -EINVAL;
79562306a36Sopenharmony_ci	}
79662306a36Sopenharmony_ci}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci#define SI1145_ST { \
79962306a36Sopenharmony_ci	.sign = 'u', \
80062306a36Sopenharmony_ci	.realbits = 16, \
80162306a36Sopenharmony_ci	.storagebits = 16, \
80262306a36Sopenharmony_ci	.endianness = IIO_LE, \
80362306a36Sopenharmony_ci}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci#define SI1145_INTENSITY_CHANNEL(_si) { \
80662306a36Sopenharmony_ci	.type = IIO_INTENSITY, \
80762306a36Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
80862306a36Sopenharmony_ci			      BIT(IIO_CHAN_INFO_OFFSET) | \
80962306a36Sopenharmony_ci			      BIT(IIO_CHAN_INFO_SCALE), \
81062306a36Sopenharmony_ci	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
81162306a36Sopenharmony_ci	.scan_type = SI1145_ST, \
81262306a36Sopenharmony_ci	.scan_index = _si, \
81362306a36Sopenharmony_ci	.address = SI1145_REG_ALSVIS_DATA, \
81462306a36Sopenharmony_ci}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci#define SI1145_INTENSITY_IR_CHANNEL(_si) { \
81762306a36Sopenharmony_ci	.type = IIO_INTENSITY, \
81862306a36Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
81962306a36Sopenharmony_ci			      BIT(IIO_CHAN_INFO_OFFSET) | \
82062306a36Sopenharmony_ci			      BIT(IIO_CHAN_INFO_SCALE), \
82162306a36Sopenharmony_ci	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
82262306a36Sopenharmony_ci	.modified = 1, \
82362306a36Sopenharmony_ci	.channel2 = IIO_MOD_LIGHT_IR, \
82462306a36Sopenharmony_ci	.scan_type = SI1145_ST, \
82562306a36Sopenharmony_ci	.scan_index = _si, \
82662306a36Sopenharmony_ci	.address = SI1145_REG_ALSIR_DATA, \
82762306a36Sopenharmony_ci}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci#define SI1145_TEMP_CHANNEL(_si) { \
83062306a36Sopenharmony_ci	.type = IIO_TEMP, \
83162306a36Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
83262306a36Sopenharmony_ci			      BIT(IIO_CHAN_INFO_OFFSET) | \
83362306a36Sopenharmony_ci			      BIT(IIO_CHAN_INFO_SCALE), \
83462306a36Sopenharmony_ci	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
83562306a36Sopenharmony_ci	.scan_type = SI1145_ST, \
83662306a36Sopenharmony_ci	.scan_index = _si, \
83762306a36Sopenharmony_ci	.address = SI1145_REG_AUX_DATA, \
83862306a36Sopenharmony_ci}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci#define SI1145_UV_CHANNEL(_si) { \
84162306a36Sopenharmony_ci	.type = IIO_UVINDEX, \
84262306a36Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
84362306a36Sopenharmony_ci			      BIT(IIO_CHAN_INFO_SCALE), \
84462306a36Sopenharmony_ci	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
84562306a36Sopenharmony_ci	.scan_type = SI1145_ST, \
84662306a36Sopenharmony_ci	.scan_index = _si, \
84762306a36Sopenharmony_ci	.address = SI1145_REG_AUX_DATA, \
84862306a36Sopenharmony_ci}
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci#define SI1145_PROXIMITY_CHANNEL(_si, _ch) { \
85162306a36Sopenharmony_ci	.type = IIO_PROXIMITY, \
85262306a36Sopenharmony_ci	.indexed = 1, \
85362306a36Sopenharmony_ci	.channel = _ch, \
85462306a36Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
85562306a36Sopenharmony_ci	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
85662306a36Sopenharmony_ci				    BIT(IIO_CHAN_INFO_OFFSET), \
85762306a36Sopenharmony_ci	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
85862306a36Sopenharmony_ci	.scan_type = SI1145_ST, \
85962306a36Sopenharmony_ci	.scan_index = _si, \
86062306a36Sopenharmony_ci	.address = SI1145_REG_PS1_DATA + _ch * 2, \
86162306a36Sopenharmony_ci}
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci#define SI1145_VOLTAGE_CHANNEL(_si) { \
86462306a36Sopenharmony_ci	.type = IIO_VOLTAGE, \
86562306a36Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
86662306a36Sopenharmony_ci	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
86762306a36Sopenharmony_ci	.scan_type = SI1145_ST, \
86862306a36Sopenharmony_ci	.scan_index = _si, \
86962306a36Sopenharmony_ci	.address = SI1145_REG_AUX_DATA, \
87062306a36Sopenharmony_ci}
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci#define SI1145_CURRENT_CHANNEL(_ch) { \
87362306a36Sopenharmony_ci	.type = IIO_CURRENT, \
87462306a36Sopenharmony_ci	.indexed = 1, \
87562306a36Sopenharmony_ci	.channel = _ch, \
87662306a36Sopenharmony_ci	.output = 1, \
87762306a36Sopenharmony_ci	.scan_index = -1, \
87862306a36Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
87962306a36Sopenharmony_ci}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_cistatic const struct iio_chan_spec si1132_channels[] = {
88262306a36Sopenharmony_ci	SI1145_INTENSITY_CHANNEL(0),
88362306a36Sopenharmony_ci	SI1145_INTENSITY_IR_CHANNEL(1),
88462306a36Sopenharmony_ci	SI1145_TEMP_CHANNEL(2),
88562306a36Sopenharmony_ci	SI1145_VOLTAGE_CHANNEL(3),
88662306a36Sopenharmony_ci	SI1145_UV_CHANNEL(4),
88762306a36Sopenharmony_ci	IIO_CHAN_SOFT_TIMESTAMP(6),
88862306a36Sopenharmony_ci};
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_cistatic const struct iio_chan_spec si1141_channels[] = {
89162306a36Sopenharmony_ci	SI1145_INTENSITY_CHANNEL(0),
89262306a36Sopenharmony_ci	SI1145_INTENSITY_IR_CHANNEL(1),
89362306a36Sopenharmony_ci	SI1145_PROXIMITY_CHANNEL(2, 0),
89462306a36Sopenharmony_ci	SI1145_TEMP_CHANNEL(3),
89562306a36Sopenharmony_ci	SI1145_VOLTAGE_CHANNEL(4),
89662306a36Sopenharmony_ci	IIO_CHAN_SOFT_TIMESTAMP(5),
89762306a36Sopenharmony_ci	SI1145_CURRENT_CHANNEL(0),
89862306a36Sopenharmony_ci};
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_cistatic const struct iio_chan_spec si1142_channels[] = {
90162306a36Sopenharmony_ci	SI1145_INTENSITY_CHANNEL(0),
90262306a36Sopenharmony_ci	SI1145_INTENSITY_IR_CHANNEL(1),
90362306a36Sopenharmony_ci	SI1145_PROXIMITY_CHANNEL(2, 0),
90462306a36Sopenharmony_ci	SI1145_PROXIMITY_CHANNEL(3, 1),
90562306a36Sopenharmony_ci	SI1145_TEMP_CHANNEL(4),
90662306a36Sopenharmony_ci	SI1145_VOLTAGE_CHANNEL(5),
90762306a36Sopenharmony_ci	IIO_CHAN_SOFT_TIMESTAMP(6),
90862306a36Sopenharmony_ci	SI1145_CURRENT_CHANNEL(0),
90962306a36Sopenharmony_ci	SI1145_CURRENT_CHANNEL(1),
91062306a36Sopenharmony_ci};
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_cistatic const struct iio_chan_spec si1143_channels[] = {
91362306a36Sopenharmony_ci	SI1145_INTENSITY_CHANNEL(0),
91462306a36Sopenharmony_ci	SI1145_INTENSITY_IR_CHANNEL(1),
91562306a36Sopenharmony_ci	SI1145_PROXIMITY_CHANNEL(2, 0),
91662306a36Sopenharmony_ci	SI1145_PROXIMITY_CHANNEL(3, 1),
91762306a36Sopenharmony_ci	SI1145_PROXIMITY_CHANNEL(4, 2),
91862306a36Sopenharmony_ci	SI1145_TEMP_CHANNEL(5),
91962306a36Sopenharmony_ci	SI1145_VOLTAGE_CHANNEL(6),
92062306a36Sopenharmony_ci	IIO_CHAN_SOFT_TIMESTAMP(7),
92162306a36Sopenharmony_ci	SI1145_CURRENT_CHANNEL(0),
92262306a36Sopenharmony_ci	SI1145_CURRENT_CHANNEL(1),
92362306a36Sopenharmony_ci	SI1145_CURRENT_CHANNEL(2),
92462306a36Sopenharmony_ci};
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_cistatic const struct iio_chan_spec si1145_channels[] = {
92762306a36Sopenharmony_ci	SI1145_INTENSITY_CHANNEL(0),
92862306a36Sopenharmony_ci	SI1145_INTENSITY_IR_CHANNEL(1),
92962306a36Sopenharmony_ci	SI1145_PROXIMITY_CHANNEL(2, 0),
93062306a36Sopenharmony_ci	SI1145_TEMP_CHANNEL(3),
93162306a36Sopenharmony_ci	SI1145_VOLTAGE_CHANNEL(4),
93262306a36Sopenharmony_ci	SI1145_UV_CHANNEL(5),
93362306a36Sopenharmony_ci	IIO_CHAN_SOFT_TIMESTAMP(6),
93462306a36Sopenharmony_ci	SI1145_CURRENT_CHANNEL(0),
93562306a36Sopenharmony_ci};
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_cistatic const struct iio_chan_spec si1146_channels[] = {
93862306a36Sopenharmony_ci	SI1145_INTENSITY_CHANNEL(0),
93962306a36Sopenharmony_ci	SI1145_INTENSITY_IR_CHANNEL(1),
94062306a36Sopenharmony_ci	SI1145_TEMP_CHANNEL(2),
94162306a36Sopenharmony_ci	SI1145_VOLTAGE_CHANNEL(3),
94262306a36Sopenharmony_ci	SI1145_UV_CHANNEL(4),
94362306a36Sopenharmony_ci	SI1145_PROXIMITY_CHANNEL(5, 0),
94462306a36Sopenharmony_ci	SI1145_PROXIMITY_CHANNEL(6, 1),
94562306a36Sopenharmony_ci	IIO_CHAN_SOFT_TIMESTAMP(7),
94662306a36Sopenharmony_ci	SI1145_CURRENT_CHANNEL(0),
94762306a36Sopenharmony_ci	SI1145_CURRENT_CHANNEL(1),
94862306a36Sopenharmony_ci};
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_cistatic const struct iio_chan_spec si1147_channels[] = {
95162306a36Sopenharmony_ci	SI1145_INTENSITY_CHANNEL(0),
95262306a36Sopenharmony_ci	SI1145_INTENSITY_IR_CHANNEL(1),
95362306a36Sopenharmony_ci	SI1145_PROXIMITY_CHANNEL(2, 0),
95462306a36Sopenharmony_ci	SI1145_PROXIMITY_CHANNEL(3, 1),
95562306a36Sopenharmony_ci	SI1145_PROXIMITY_CHANNEL(4, 2),
95662306a36Sopenharmony_ci	SI1145_TEMP_CHANNEL(5),
95762306a36Sopenharmony_ci	SI1145_VOLTAGE_CHANNEL(6),
95862306a36Sopenharmony_ci	SI1145_UV_CHANNEL(7),
95962306a36Sopenharmony_ci	IIO_CHAN_SOFT_TIMESTAMP(8),
96062306a36Sopenharmony_ci	SI1145_CURRENT_CHANNEL(0),
96162306a36Sopenharmony_ci	SI1145_CURRENT_CHANNEL(1),
96262306a36Sopenharmony_ci	SI1145_CURRENT_CHANNEL(2),
96362306a36Sopenharmony_ci};
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_cistatic struct attribute *si1132_attributes[] = {
96662306a36Sopenharmony_ci	&iio_const_attr_in_intensity_scale_available.dev_attr.attr,
96762306a36Sopenharmony_ci	&iio_const_attr_in_intensity_ir_scale_available.dev_attr.attr,
96862306a36Sopenharmony_ci	NULL,
96962306a36Sopenharmony_ci};
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_cistatic struct attribute *si114x_attributes[] = {
97262306a36Sopenharmony_ci	&iio_const_attr_in_intensity_scale_available.dev_attr.attr,
97362306a36Sopenharmony_ci	&iio_const_attr_in_intensity_ir_scale_available.dev_attr.attr,
97462306a36Sopenharmony_ci	&iio_const_attr_in_proximity_scale_available.dev_attr.attr,
97562306a36Sopenharmony_ci	NULL,
97662306a36Sopenharmony_ci};
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_cistatic const struct attribute_group si1132_attribute_group = {
97962306a36Sopenharmony_ci	.attrs = si1132_attributes,
98062306a36Sopenharmony_ci};
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_cistatic const struct attribute_group si114x_attribute_group = {
98362306a36Sopenharmony_ci	.attrs = si114x_attributes,
98462306a36Sopenharmony_ci};
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_cistatic const struct iio_info si1132_info = {
98862306a36Sopenharmony_ci	.read_raw = si1145_read_raw,
98962306a36Sopenharmony_ci	.write_raw = si1145_write_raw,
99062306a36Sopenharmony_ci	.attrs = &si1132_attribute_group,
99162306a36Sopenharmony_ci};
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_cistatic const struct iio_info si114x_info = {
99462306a36Sopenharmony_ci	.read_raw = si1145_read_raw,
99562306a36Sopenharmony_ci	.write_raw = si1145_write_raw,
99662306a36Sopenharmony_ci	.attrs = &si114x_attribute_group,
99762306a36Sopenharmony_ci};
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci#define SI1145_PART(id, iio_info, chans, leds, uncompressed_meas_rate) \
100062306a36Sopenharmony_ci	{id, iio_info, chans, ARRAY_SIZE(chans), leds, uncompressed_meas_rate}
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_cistatic const struct si1145_part_info si1145_part_info[] = {
100362306a36Sopenharmony_ci	[SI1132] = SI1145_PART(0x32, &si1132_info, si1132_channels, 0, true),
100462306a36Sopenharmony_ci	[SI1141] = SI1145_PART(0x41, &si114x_info, si1141_channels, 1, false),
100562306a36Sopenharmony_ci	[SI1142] = SI1145_PART(0x42, &si114x_info, si1142_channels, 2, false),
100662306a36Sopenharmony_ci	[SI1143] = SI1145_PART(0x43, &si114x_info, si1143_channels, 3, false),
100762306a36Sopenharmony_ci	[SI1145] = SI1145_PART(0x45, &si114x_info, si1145_channels, 1, true),
100862306a36Sopenharmony_ci	[SI1146] = SI1145_PART(0x46, &si114x_info, si1146_channels, 2, true),
100962306a36Sopenharmony_ci	[SI1147] = SI1145_PART(0x47, &si114x_info, si1147_channels, 3, true),
101062306a36Sopenharmony_ci};
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_cistatic int si1145_initialize(struct si1145_data *data)
101362306a36Sopenharmony_ci{
101462306a36Sopenharmony_ci	struct i2c_client *client = data->client;
101562306a36Sopenharmony_ci	int ret;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	ret = i2c_smbus_write_byte_data(client, SI1145_REG_COMMAND,
101862306a36Sopenharmony_ci					SI1145_CMD_RESET);
101962306a36Sopenharmony_ci	if (ret < 0)
102062306a36Sopenharmony_ci		return ret;
102162306a36Sopenharmony_ci	msleep(SI1145_COMMAND_TIMEOUT_MS);
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	/* Hardware key, magic value */
102462306a36Sopenharmony_ci	ret = i2c_smbus_write_byte_data(client, SI1145_REG_HW_KEY, 0x17);
102562306a36Sopenharmony_ci	if (ret < 0)
102662306a36Sopenharmony_ci		return ret;
102762306a36Sopenharmony_ci	msleep(SI1145_COMMAND_TIMEOUT_MS);
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	/* Turn off autonomous mode */
103062306a36Sopenharmony_ci	ret = si1145_set_meas_rate(data, 0);
103162306a36Sopenharmony_ci	if (ret < 0)
103262306a36Sopenharmony_ci		return ret;
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	/* Initialize sampling freq to 10 Hz */
103562306a36Sopenharmony_ci	ret = si1145_store_samp_freq(data, 10);
103662306a36Sopenharmony_ci	if (ret < 0)
103762306a36Sopenharmony_ci		return ret;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	/* Set LED currents to 45 mA; have 4 bits, see Table 2 in datasheet */
104062306a36Sopenharmony_ci	switch (data->part_info->num_leds) {
104162306a36Sopenharmony_ci	case 3:
104262306a36Sopenharmony_ci		ret = i2c_smbus_write_byte_data(client,
104362306a36Sopenharmony_ci						SI1145_REG_PS_LED3,
104462306a36Sopenharmony_ci						SI1145_LED_CURRENT_45mA);
104562306a36Sopenharmony_ci		if (ret < 0)
104662306a36Sopenharmony_ci			return ret;
104762306a36Sopenharmony_ci		fallthrough;
104862306a36Sopenharmony_ci	case 2:
104962306a36Sopenharmony_ci		ret = i2c_smbus_write_byte_data(client,
105062306a36Sopenharmony_ci						SI1145_REG_PS_LED21,
105162306a36Sopenharmony_ci						(SI1145_LED_CURRENT_45mA << 4) |
105262306a36Sopenharmony_ci						SI1145_LED_CURRENT_45mA);
105362306a36Sopenharmony_ci		break;
105462306a36Sopenharmony_ci	case 1:
105562306a36Sopenharmony_ci		ret = i2c_smbus_write_byte_data(client,
105662306a36Sopenharmony_ci						SI1145_REG_PS_LED21,
105762306a36Sopenharmony_ci						SI1145_LED_CURRENT_45mA);
105862306a36Sopenharmony_ci		break;
105962306a36Sopenharmony_ci	default:
106062306a36Sopenharmony_ci		ret = 0;
106162306a36Sopenharmony_ci		break;
106262306a36Sopenharmony_ci	}
106362306a36Sopenharmony_ci	if (ret < 0)
106462306a36Sopenharmony_ci		return ret;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	/* Set normal proximity measurement mode */
106762306a36Sopenharmony_ci	ret = si1145_param_set(data, SI1145_PARAM_PS_ADC_MISC,
106862306a36Sopenharmony_ci			       SI1145_PS_ADC_MODE_NORMAL);
106962306a36Sopenharmony_ci	if (ret < 0)
107062306a36Sopenharmony_ci		return ret;
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	ret = si1145_param_set(data, SI1145_PARAM_PS_ADC_GAIN, 0x01);
107362306a36Sopenharmony_ci	if (ret < 0)
107462306a36Sopenharmony_ci		return ret;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	/* ADC_COUNTER should be one complement of ADC_GAIN */
107762306a36Sopenharmony_ci	ret = si1145_param_set(data, SI1145_PARAM_PS_ADC_COUNTER, 0x06 << 4);
107862306a36Sopenharmony_ci	if (ret < 0)
107962306a36Sopenharmony_ci		return ret;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	/* Set ALS visible measurement mode */
108262306a36Sopenharmony_ci	ret = si1145_param_set(data, SI1145_PARAM_ALSVIS_ADC_MISC,
108362306a36Sopenharmony_ci			       SI1145_ADC_MISC_RANGE);
108462306a36Sopenharmony_ci	if (ret < 0)
108562306a36Sopenharmony_ci		return ret;
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	ret = si1145_param_set(data, SI1145_PARAM_ALSVIS_ADC_GAIN, 0x03);
108862306a36Sopenharmony_ci	if (ret < 0)
108962306a36Sopenharmony_ci		return ret;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	ret = si1145_param_set(data, SI1145_PARAM_ALSVIS_ADC_COUNTER,
109262306a36Sopenharmony_ci			       0x04 << 4);
109362306a36Sopenharmony_ci	if (ret < 0)
109462306a36Sopenharmony_ci		return ret;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	/* Set ALS IR measurement mode */
109762306a36Sopenharmony_ci	ret = si1145_param_set(data, SI1145_PARAM_ALSIR_ADC_MISC,
109862306a36Sopenharmony_ci			       SI1145_ADC_MISC_RANGE);
109962306a36Sopenharmony_ci	if (ret < 0)
110062306a36Sopenharmony_ci		return ret;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	ret = si1145_param_set(data, SI1145_PARAM_ALSIR_ADC_GAIN, 0x01);
110362306a36Sopenharmony_ci	if (ret < 0)
110462306a36Sopenharmony_ci		return ret;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	ret = si1145_param_set(data, SI1145_PARAM_ALSIR_ADC_COUNTER,
110762306a36Sopenharmony_ci			       0x06 << 4);
110862306a36Sopenharmony_ci	if (ret < 0)
110962306a36Sopenharmony_ci		return ret;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	/*
111262306a36Sopenharmony_ci	 * Initialize UCOEF to default values in datasheet
111362306a36Sopenharmony_ci	 * These registers are normally zero on reset
111462306a36Sopenharmony_ci	 */
111562306a36Sopenharmony_ci	if (data->part_info == &si1145_part_info[SI1132] ||
111662306a36Sopenharmony_ci		data->part_info == &si1145_part_info[SI1145] ||
111762306a36Sopenharmony_ci		data->part_info == &si1145_part_info[SI1146] ||
111862306a36Sopenharmony_ci		data->part_info == &si1145_part_info[SI1147]) {
111962306a36Sopenharmony_ci		ret = i2c_smbus_write_byte_data(data->client,
112062306a36Sopenharmony_ci						SI1145_REG_UCOEF1,
112162306a36Sopenharmony_ci						SI1145_UCOEF1_DEFAULT);
112262306a36Sopenharmony_ci		if (ret < 0)
112362306a36Sopenharmony_ci			return ret;
112462306a36Sopenharmony_ci		ret = i2c_smbus_write_byte_data(data->client,
112562306a36Sopenharmony_ci				SI1145_REG_UCOEF2, SI1145_UCOEF2_DEFAULT);
112662306a36Sopenharmony_ci		if (ret < 0)
112762306a36Sopenharmony_ci			return ret;
112862306a36Sopenharmony_ci		ret = i2c_smbus_write_byte_data(data->client,
112962306a36Sopenharmony_ci				SI1145_REG_UCOEF3, SI1145_UCOEF3_DEFAULT);
113062306a36Sopenharmony_ci		if (ret < 0)
113162306a36Sopenharmony_ci			return ret;
113262306a36Sopenharmony_ci		ret = i2c_smbus_write_byte_data(data->client,
113362306a36Sopenharmony_ci				SI1145_REG_UCOEF4, SI1145_UCOEF4_DEFAULT);
113462306a36Sopenharmony_ci		if (ret < 0)
113562306a36Sopenharmony_ci			return ret;
113662306a36Sopenharmony_ci	}
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	return 0;
113962306a36Sopenharmony_ci}
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci/*
114262306a36Sopenharmony_ci * Program the channels we want to measure with CMD_PSALS_AUTO. No need for
114362306a36Sopenharmony_ci * _postdisable as we stop with CMD_PSALS_PAUSE; single measurement (direct)
114462306a36Sopenharmony_ci * mode reprograms the channels list anyway...
114562306a36Sopenharmony_ci */
114662306a36Sopenharmony_cistatic int si1145_buffer_preenable(struct iio_dev *indio_dev)
114762306a36Sopenharmony_ci{
114862306a36Sopenharmony_ci	struct si1145_data *data = iio_priv(indio_dev);
114962306a36Sopenharmony_ci	int ret;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	mutex_lock(&data->lock);
115262306a36Sopenharmony_ci	ret = si1145_set_chlist(indio_dev, *indio_dev->active_scan_mask);
115362306a36Sopenharmony_ci	mutex_unlock(&data->lock);
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	return ret;
115662306a36Sopenharmony_ci}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_cistatic bool si1145_validate_scan_mask(struct iio_dev *indio_dev,
115962306a36Sopenharmony_ci			       const unsigned long *scan_mask)
116062306a36Sopenharmony_ci{
116162306a36Sopenharmony_ci	struct si1145_data *data = iio_priv(indio_dev);
116262306a36Sopenharmony_ci	unsigned int count = 0;
116362306a36Sopenharmony_ci	int i;
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	/* Check that at most one AUX channel is enabled */
116662306a36Sopenharmony_ci	for_each_set_bit(i, scan_mask, data->part_info->num_channels) {
116762306a36Sopenharmony_ci		if (indio_dev->channels[i].address == SI1145_REG_AUX_DATA)
116862306a36Sopenharmony_ci			count++;
116962306a36Sopenharmony_ci	}
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	return count <= 1;
117262306a36Sopenharmony_ci}
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_cistatic const struct iio_buffer_setup_ops si1145_buffer_setup_ops = {
117562306a36Sopenharmony_ci	.preenable = si1145_buffer_preenable,
117662306a36Sopenharmony_ci	.validate_scan_mask = si1145_validate_scan_mask,
117762306a36Sopenharmony_ci};
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci/*
118062306a36Sopenharmony_ci * si1145_trigger_set_state() - Set trigger state
118162306a36Sopenharmony_ci *
118262306a36Sopenharmony_ci * When not using triggers interrupts are disabled and measurement rate is
118362306a36Sopenharmony_ci * set to zero in order to minimize power consumption.
118462306a36Sopenharmony_ci */
118562306a36Sopenharmony_cistatic int si1145_trigger_set_state(struct iio_trigger *trig, bool state)
118662306a36Sopenharmony_ci{
118762306a36Sopenharmony_ci	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
118862306a36Sopenharmony_ci	struct si1145_data *data = iio_priv(indio_dev);
118962306a36Sopenharmony_ci	int err = 0, ret;
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	mutex_lock(&data->lock);
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	if (state) {
119462306a36Sopenharmony_ci		data->autonomous = true;
119562306a36Sopenharmony_ci		err = i2c_smbus_write_byte_data(data->client,
119662306a36Sopenharmony_ci				SI1145_REG_INT_CFG, SI1145_INT_CFG_OE);
119762306a36Sopenharmony_ci		if (err < 0)
119862306a36Sopenharmony_ci			goto disable;
119962306a36Sopenharmony_ci		err = i2c_smbus_write_byte_data(data->client,
120062306a36Sopenharmony_ci				SI1145_REG_IRQ_ENABLE, SI1145_MASK_ALL_IE);
120162306a36Sopenharmony_ci		if (err < 0)
120262306a36Sopenharmony_ci			goto disable;
120362306a36Sopenharmony_ci		err = si1145_set_meas_rate(data, data->meas_rate);
120462306a36Sopenharmony_ci		if (err < 0)
120562306a36Sopenharmony_ci			goto disable;
120662306a36Sopenharmony_ci		err = si1145_command(data, SI1145_CMD_PSALS_AUTO);
120762306a36Sopenharmony_ci		if (err < 0)
120862306a36Sopenharmony_ci			goto disable;
120962306a36Sopenharmony_ci	} else {
121062306a36Sopenharmony_cidisable:
121162306a36Sopenharmony_ci		/* Disable as much as possible skipping errors */
121262306a36Sopenharmony_ci		ret = si1145_command(data, SI1145_CMD_PSALS_PAUSE);
121362306a36Sopenharmony_ci		if (ret < 0 && !err)
121462306a36Sopenharmony_ci			err = ret;
121562306a36Sopenharmony_ci		ret = si1145_set_meas_rate(data, 0);
121662306a36Sopenharmony_ci		if (ret < 0 && !err)
121762306a36Sopenharmony_ci			err = ret;
121862306a36Sopenharmony_ci		ret = i2c_smbus_write_byte_data(data->client,
121962306a36Sopenharmony_ci						SI1145_REG_IRQ_ENABLE, 0);
122062306a36Sopenharmony_ci		if (ret < 0 && !err)
122162306a36Sopenharmony_ci			err = ret;
122262306a36Sopenharmony_ci		ret = i2c_smbus_write_byte_data(data->client,
122362306a36Sopenharmony_ci						SI1145_REG_INT_CFG, 0);
122462306a36Sopenharmony_ci		if (ret < 0 && !err)
122562306a36Sopenharmony_ci			err = ret;
122662306a36Sopenharmony_ci		data->autonomous = false;
122762306a36Sopenharmony_ci	}
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	mutex_unlock(&data->lock);
123062306a36Sopenharmony_ci	return err;
123162306a36Sopenharmony_ci}
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_cistatic const struct iio_trigger_ops si1145_trigger_ops = {
123462306a36Sopenharmony_ci	.set_trigger_state = si1145_trigger_set_state,
123562306a36Sopenharmony_ci};
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_cistatic int si1145_probe_trigger(struct iio_dev *indio_dev)
123862306a36Sopenharmony_ci{
123962306a36Sopenharmony_ci	struct si1145_data *data = iio_priv(indio_dev);
124062306a36Sopenharmony_ci	struct i2c_client *client = data->client;
124162306a36Sopenharmony_ci	struct iio_trigger *trig;
124262306a36Sopenharmony_ci	int ret;
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	trig = devm_iio_trigger_alloc(&client->dev,
124562306a36Sopenharmony_ci			"%s-dev%d", indio_dev->name, iio_device_id(indio_dev));
124662306a36Sopenharmony_ci	if (!trig)
124762306a36Sopenharmony_ci		return -ENOMEM;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	trig->ops = &si1145_trigger_ops;
125062306a36Sopenharmony_ci	iio_trigger_set_drvdata(trig, indio_dev);
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	ret = devm_request_irq(&client->dev, client->irq,
125362306a36Sopenharmony_ci			  iio_trigger_generic_data_rdy_poll,
125462306a36Sopenharmony_ci			  IRQF_TRIGGER_FALLING,
125562306a36Sopenharmony_ci			  "si1145_irq",
125662306a36Sopenharmony_ci			  trig);
125762306a36Sopenharmony_ci	if (ret < 0) {
125862306a36Sopenharmony_ci		dev_err(&client->dev, "irq request failed\n");
125962306a36Sopenharmony_ci		return ret;
126062306a36Sopenharmony_ci	}
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	ret = devm_iio_trigger_register(&client->dev, trig);
126362306a36Sopenharmony_ci	if (ret)
126462306a36Sopenharmony_ci		return ret;
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	data->trig = trig;
126762306a36Sopenharmony_ci	indio_dev->trig = iio_trigger_get(data->trig);
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	return 0;
127062306a36Sopenharmony_ci}
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_cistatic int si1145_probe(struct i2c_client *client)
127362306a36Sopenharmony_ci{
127462306a36Sopenharmony_ci	const struct i2c_device_id *id = i2c_client_get_device_id(client);
127562306a36Sopenharmony_ci	struct si1145_data *data;
127662306a36Sopenharmony_ci	struct iio_dev *indio_dev;
127762306a36Sopenharmony_ci	u8 part_id, rev_id, seq_id;
127862306a36Sopenharmony_ci	int ret;
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
128162306a36Sopenharmony_ci	if (!indio_dev)
128262306a36Sopenharmony_ci		return -ENOMEM;
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	data = iio_priv(indio_dev);
128562306a36Sopenharmony_ci	i2c_set_clientdata(client, indio_dev);
128662306a36Sopenharmony_ci	data->client = client;
128762306a36Sopenharmony_ci	data->part_info = &si1145_part_info[id->driver_data];
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	part_id = ret = i2c_smbus_read_byte_data(data->client,
129062306a36Sopenharmony_ci						 SI1145_REG_PART_ID);
129162306a36Sopenharmony_ci	if (ret < 0)
129262306a36Sopenharmony_ci		return ret;
129362306a36Sopenharmony_ci	rev_id = ret = i2c_smbus_read_byte_data(data->client,
129462306a36Sopenharmony_ci						SI1145_REG_REV_ID);
129562306a36Sopenharmony_ci	if (ret < 0)
129662306a36Sopenharmony_ci		return ret;
129762306a36Sopenharmony_ci	seq_id = ret = i2c_smbus_read_byte_data(data->client,
129862306a36Sopenharmony_ci						SI1145_REG_SEQ_ID);
129962306a36Sopenharmony_ci	if (ret < 0)
130062306a36Sopenharmony_ci		return ret;
130162306a36Sopenharmony_ci	dev_info(&client->dev, "device ID part 0x%02x rev 0x%02x seq 0x%02x\n",
130262306a36Sopenharmony_ci			part_id, rev_id, seq_id);
130362306a36Sopenharmony_ci	if (part_id != data->part_info->part) {
130462306a36Sopenharmony_ci		dev_err(&client->dev, "part ID mismatch got 0x%02x, expected 0x%02x\n",
130562306a36Sopenharmony_ci				part_id, data->part_info->part);
130662306a36Sopenharmony_ci		return -ENODEV;
130762306a36Sopenharmony_ci	}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	indio_dev->name = id->name;
131062306a36Sopenharmony_ci	indio_dev->channels = data->part_info->channels;
131162306a36Sopenharmony_ci	indio_dev->num_channels = data->part_info->num_channels;
131262306a36Sopenharmony_ci	indio_dev->info = data->part_info->iio_info;
131362306a36Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	mutex_init(&data->lock);
131662306a36Sopenharmony_ci	mutex_init(&data->cmdlock);
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	ret = si1145_initialize(data);
131962306a36Sopenharmony_ci	if (ret < 0)
132062306a36Sopenharmony_ci		return ret;
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	ret = devm_iio_triggered_buffer_setup(&client->dev,
132362306a36Sopenharmony_ci		indio_dev, NULL,
132462306a36Sopenharmony_ci		si1145_trigger_handler, &si1145_buffer_setup_ops);
132562306a36Sopenharmony_ci	if (ret < 0)
132662306a36Sopenharmony_ci		return ret;
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	if (client->irq) {
132962306a36Sopenharmony_ci		ret = si1145_probe_trigger(indio_dev);
133062306a36Sopenharmony_ci		if (ret < 0)
133162306a36Sopenharmony_ci			return ret;
133262306a36Sopenharmony_ci	} else {
133362306a36Sopenharmony_ci		dev_info(&client->dev, "no irq, using polling\n");
133462306a36Sopenharmony_ci	}
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	return devm_iio_device_register(&client->dev, indio_dev);
133762306a36Sopenharmony_ci}
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_cistatic const struct i2c_device_id si1145_ids[] = {
134062306a36Sopenharmony_ci	{ "si1132", SI1132 },
134162306a36Sopenharmony_ci	{ "si1141", SI1141 },
134262306a36Sopenharmony_ci	{ "si1142", SI1142 },
134362306a36Sopenharmony_ci	{ "si1143", SI1143 },
134462306a36Sopenharmony_ci	{ "si1145", SI1145 },
134562306a36Sopenharmony_ci	{ "si1146", SI1146 },
134662306a36Sopenharmony_ci	{ "si1147", SI1147 },
134762306a36Sopenharmony_ci	{ }
134862306a36Sopenharmony_ci};
134962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, si1145_ids);
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_cistatic struct i2c_driver si1145_driver = {
135262306a36Sopenharmony_ci	.driver = {
135362306a36Sopenharmony_ci		.name   = "si1145",
135462306a36Sopenharmony_ci	},
135562306a36Sopenharmony_ci	.probe = si1145_probe,
135662306a36Sopenharmony_ci	.id_table = si1145_ids,
135762306a36Sopenharmony_ci};
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_cimodule_i2c_driver(si1145_driver);
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ciMODULE_AUTHOR("Peter Meerwald-Stadler <pmeerw@pmeerw.net>");
136262306a36Sopenharmony_ciMODULE_DESCRIPTION("Silabs SI1132 and SI1141/2/3/5/6/7 proximity, ambient light and UV index sensor driver");
136362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1364