162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * isl29501.c: ISL29501 Time of Flight sensor driver.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2018
662306a36Sopenharmony_ci * Author: Mathieu Othacehe <m.othacehe@gmail.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * 7-bit I2C slave address: 0x57
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/i2c.h>
1462306a36Sopenharmony_ci#include <linux/err.h>
1562306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
1662306a36Sopenharmony_ci#include <linux/iio/iio.h>
1762306a36Sopenharmony_ci#include <linux/iio/sysfs.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/iio/trigger_consumer.h>
2062306a36Sopenharmony_ci#include <linux/iio/buffer.h>
2162306a36Sopenharmony_ci#include <linux/iio/triggered_buffer.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* Control, setting and status registers */
2462306a36Sopenharmony_ci#define ISL29501_DEVICE_ID			0x00
2562306a36Sopenharmony_ci#define ISL29501_ID				0x0A
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/* Sampling control registers */
2862306a36Sopenharmony_ci#define ISL29501_INTEGRATION_PERIOD		0x10
2962306a36Sopenharmony_ci#define ISL29501_SAMPLE_PERIOD			0x11
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/* Closed loop calibration registers */
3262306a36Sopenharmony_ci#define ISL29501_CROSSTALK_I_MSB		0x24
3362306a36Sopenharmony_ci#define ISL29501_CROSSTALK_I_LSB		0x25
3462306a36Sopenharmony_ci#define ISL29501_CROSSTALK_I_EXPONENT		0x26
3562306a36Sopenharmony_ci#define ISL29501_CROSSTALK_Q_MSB		0x27
3662306a36Sopenharmony_ci#define ISL29501_CROSSTALK_Q_LSB		0x28
3762306a36Sopenharmony_ci#define ISL29501_CROSSTALK_Q_EXPONENT		0x29
3862306a36Sopenharmony_ci#define ISL29501_CROSSTALK_GAIN_MSB		0x2A
3962306a36Sopenharmony_ci#define ISL29501_CROSSTALK_GAIN_LSB		0x2B
4062306a36Sopenharmony_ci#define ISL29501_MAGNITUDE_REF_EXP		0x2C
4162306a36Sopenharmony_ci#define ISL29501_MAGNITUDE_REF_MSB		0x2D
4262306a36Sopenharmony_ci#define ISL29501_MAGNITUDE_REF_LSB		0x2E
4362306a36Sopenharmony_ci#define ISL29501_PHASE_OFFSET_MSB		0x2F
4462306a36Sopenharmony_ci#define ISL29501_PHASE_OFFSET_LSB		0x30
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/* Analog control registers */
4762306a36Sopenharmony_ci#define ISL29501_DRIVER_RANGE			0x90
4862306a36Sopenharmony_ci#define ISL29501_EMITTER_DAC			0x91
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#define ISL29501_COMMAND_REGISTER		0xB0
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/* Commands */
5362306a36Sopenharmony_ci#define ISL29501_EMUL_SAMPLE_START_PIN		0x49
5462306a36Sopenharmony_ci#define ISL29501_RESET_ALL_REGISTERS		0xD7
5562306a36Sopenharmony_ci#define ISL29501_RESET_INT_SM			0xD1
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/* Ambiant light and temperature corrections */
5862306a36Sopenharmony_ci#define ISL29501_TEMP_REFERENCE			0x31
5962306a36Sopenharmony_ci#define ISL29501_PHASE_EXPONENT			0x33
6062306a36Sopenharmony_ci#define ISL29501_TEMP_COEFF_A			0x34
6162306a36Sopenharmony_ci#define ISL29501_TEMP_COEFF_B			0x39
6262306a36Sopenharmony_ci#define ISL29501_AMBIANT_COEFF_A		0x36
6362306a36Sopenharmony_ci#define ISL29501_AMBIANT_COEFF_B		0x3B
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/* Data output registers */
6662306a36Sopenharmony_ci#define ISL29501_DISTANCE_MSB_DATA		0xD1
6762306a36Sopenharmony_ci#define ISL29501_DISTANCE_LSB_DATA		0xD2
6862306a36Sopenharmony_ci#define ISL29501_PRECISION_MSB			0xD3
6962306a36Sopenharmony_ci#define ISL29501_PRECISION_LSB			0xD4
7062306a36Sopenharmony_ci#define ISL29501_MAGNITUDE_EXPONENT		0xD5
7162306a36Sopenharmony_ci#define ISL29501_MAGNITUDE_MSB			0xD6
7262306a36Sopenharmony_ci#define ISL29501_MAGNITUDE_LSB			0xD7
7362306a36Sopenharmony_ci#define ISL29501_PHASE_MSB			0xD8
7462306a36Sopenharmony_ci#define ISL29501_PHASE_LSB			0xD9
7562306a36Sopenharmony_ci#define ISL29501_I_RAW_EXPONENT			0xDA
7662306a36Sopenharmony_ci#define ISL29501_I_RAW_MSB			0xDB
7762306a36Sopenharmony_ci#define ISL29501_I_RAW_LSB			0xDC
7862306a36Sopenharmony_ci#define ISL29501_Q_RAW_EXPONENT			0xDD
7962306a36Sopenharmony_ci#define ISL29501_Q_RAW_MSB			0xDE
8062306a36Sopenharmony_ci#define ISL29501_Q_RAW_LSB			0xDF
8162306a36Sopenharmony_ci#define ISL29501_DIE_TEMPERATURE		0xE2
8262306a36Sopenharmony_ci#define ISL29501_AMBIENT_LIGHT			0xE3
8362306a36Sopenharmony_ci#define ISL29501_GAIN_MSB			0xE6
8462306a36Sopenharmony_ci#define ISL29501_GAIN_LSB			0xE7
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci#define ISL29501_MAX_EXP_VAL 15
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci#define ISL29501_INT_TIME_AVAILABLE \
8962306a36Sopenharmony_ci	"0.00007 0.00014 0.00028 0.00057 0.00114 " \
9062306a36Sopenharmony_ci	"0.00228 0.00455 0.00910 0.01820 0.03640 " \
9162306a36Sopenharmony_ci	"0.07281 0.14561"
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define ISL29501_CURRENT_SCALE_AVAILABLE \
9462306a36Sopenharmony_ci	"0.0039 0.0078 0.0118 0.0157 0.0196 " \
9562306a36Sopenharmony_ci	"0.0235 0.0275 0.0314 0.0352 0.0392 " \
9662306a36Sopenharmony_ci	"0.0431 0.0471 0.0510 0.0549 0.0588"
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cienum isl29501_correction_coeff {
9962306a36Sopenharmony_ci	COEFF_TEMP_A,
10062306a36Sopenharmony_ci	COEFF_TEMP_B,
10162306a36Sopenharmony_ci	COEFF_LIGHT_A,
10262306a36Sopenharmony_ci	COEFF_LIGHT_B,
10362306a36Sopenharmony_ci	COEFF_MAX,
10462306a36Sopenharmony_ci};
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistruct isl29501_private {
10762306a36Sopenharmony_ci	struct i2c_client *client;
10862306a36Sopenharmony_ci	struct mutex lock;
10962306a36Sopenharmony_ci	/* Exact representation of correction coefficients. */
11062306a36Sopenharmony_ci	unsigned int shadow_coeffs[COEFF_MAX];
11162306a36Sopenharmony_ci};
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cienum isl29501_register_name {
11462306a36Sopenharmony_ci	REG_DISTANCE,
11562306a36Sopenharmony_ci	REG_PHASE,
11662306a36Sopenharmony_ci	REG_TEMPERATURE,
11762306a36Sopenharmony_ci	REG_AMBIENT_LIGHT,
11862306a36Sopenharmony_ci	REG_GAIN,
11962306a36Sopenharmony_ci	REG_GAIN_BIAS,
12062306a36Sopenharmony_ci	REG_PHASE_EXP,
12162306a36Sopenharmony_ci	REG_CALIB_PHASE_TEMP_A,
12262306a36Sopenharmony_ci	REG_CALIB_PHASE_TEMP_B,
12362306a36Sopenharmony_ci	REG_CALIB_PHASE_LIGHT_A,
12462306a36Sopenharmony_ci	REG_CALIB_PHASE_LIGHT_B,
12562306a36Sopenharmony_ci	REG_DISTANCE_BIAS,
12662306a36Sopenharmony_ci	REG_TEMPERATURE_BIAS,
12762306a36Sopenharmony_ci	REG_INT_TIME,
12862306a36Sopenharmony_ci	REG_SAMPLE_TIME,
12962306a36Sopenharmony_ci	REG_DRIVER_RANGE,
13062306a36Sopenharmony_ci	REG_EMITTER_DAC,
13162306a36Sopenharmony_ci};
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistruct isl29501_register_desc {
13462306a36Sopenharmony_ci	u8 msb;
13562306a36Sopenharmony_ci	u8 lsb;
13662306a36Sopenharmony_ci};
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic const struct isl29501_register_desc isl29501_registers[] = {
13962306a36Sopenharmony_ci	[REG_DISTANCE] = {
14062306a36Sopenharmony_ci		.msb = ISL29501_DISTANCE_MSB_DATA,
14162306a36Sopenharmony_ci		.lsb = ISL29501_DISTANCE_LSB_DATA,
14262306a36Sopenharmony_ci	},
14362306a36Sopenharmony_ci	[REG_PHASE] = {
14462306a36Sopenharmony_ci		.msb = ISL29501_PHASE_MSB,
14562306a36Sopenharmony_ci		.lsb = ISL29501_PHASE_LSB,
14662306a36Sopenharmony_ci	},
14762306a36Sopenharmony_ci	[REG_TEMPERATURE] = {
14862306a36Sopenharmony_ci		.lsb = ISL29501_DIE_TEMPERATURE,
14962306a36Sopenharmony_ci	},
15062306a36Sopenharmony_ci	[REG_AMBIENT_LIGHT] = {
15162306a36Sopenharmony_ci		.lsb = ISL29501_AMBIENT_LIGHT,
15262306a36Sopenharmony_ci	},
15362306a36Sopenharmony_ci	[REG_GAIN] = {
15462306a36Sopenharmony_ci		.msb = ISL29501_GAIN_MSB,
15562306a36Sopenharmony_ci		.lsb = ISL29501_GAIN_LSB,
15662306a36Sopenharmony_ci	},
15762306a36Sopenharmony_ci	[REG_GAIN_BIAS] = {
15862306a36Sopenharmony_ci		.msb = ISL29501_CROSSTALK_GAIN_MSB,
15962306a36Sopenharmony_ci		.lsb = ISL29501_CROSSTALK_GAIN_LSB,
16062306a36Sopenharmony_ci	},
16162306a36Sopenharmony_ci	[REG_PHASE_EXP] = {
16262306a36Sopenharmony_ci		.lsb = ISL29501_PHASE_EXPONENT,
16362306a36Sopenharmony_ci	},
16462306a36Sopenharmony_ci	[REG_CALIB_PHASE_TEMP_A] = {
16562306a36Sopenharmony_ci		.lsb = ISL29501_TEMP_COEFF_A,
16662306a36Sopenharmony_ci	},
16762306a36Sopenharmony_ci	[REG_CALIB_PHASE_TEMP_B] = {
16862306a36Sopenharmony_ci		.lsb = ISL29501_TEMP_COEFF_B,
16962306a36Sopenharmony_ci	},
17062306a36Sopenharmony_ci	[REG_CALIB_PHASE_LIGHT_A] = {
17162306a36Sopenharmony_ci		.lsb = ISL29501_AMBIANT_COEFF_A,
17262306a36Sopenharmony_ci	},
17362306a36Sopenharmony_ci	[REG_CALIB_PHASE_LIGHT_B] = {
17462306a36Sopenharmony_ci		.lsb = ISL29501_AMBIANT_COEFF_B,
17562306a36Sopenharmony_ci	},
17662306a36Sopenharmony_ci	[REG_DISTANCE_BIAS] = {
17762306a36Sopenharmony_ci		.msb = ISL29501_PHASE_OFFSET_MSB,
17862306a36Sopenharmony_ci		.lsb = ISL29501_PHASE_OFFSET_LSB,
17962306a36Sopenharmony_ci	},
18062306a36Sopenharmony_ci	[REG_TEMPERATURE_BIAS] = {
18162306a36Sopenharmony_ci		.lsb = ISL29501_TEMP_REFERENCE,
18262306a36Sopenharmony_ci	},
18362306a36Sopenharmony_ci	[REG_INT_TIME] = {
18462306a36Sopenharmony_ci		.lsb = ISL29501_INTEGRATION_PERIOD,
18562306a36Sopenharmony_ci	},
18662306a36Sopenharmony_ci	[REG_SAMPLE_TIME] = {
18762306a36Sopenharmony_ci		.lsb = ISL29501_SAMPLE_PERIOD,
18862306a36Sopenharmony_ci	},
18962306a36Sopenharmony_ci	[REG_DRIVER_RANGE] = {
19062306a36Sopenharmony_ci		.lsb = ISL29501_DRIVER_RANGE,
19162306a36Sopenharmony_ci	},
19262306a36Sopenharmony_ci	[REG_EMITTER_DAC] = {
19362306a36Sopenharmony_ci		.lsb = ISL29501_EMITTER_DAC,
19462306a36Sopenharmony_ci	},
19562306a36Sopenharmony_ci};
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic int isl29501_register_read(struct isl29501_private *isl29501,
19862306a36Sopenharmony_ci				  enum isl29501_register_name name,
19962306a36Sopenharmony_ci				  u32 *val)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	const struct isl29501_register_desc *reg = &isl29501_registers[name];
20262306a36Sopenharmony_ci	u8 msb = 0, lsb = 0;
20362306a36Sopenharmony_ci	s32 ret;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	mutex_lock(&isl29501->lock);
20662306a36Sopenharmony_ci	if (reg->msb) {
20762306a36Sopenharmony_ci		ret = i2c_smbus_read_byte_data(isl29501->client, reg->msb);
20862306a36Sopenharmony_ci		if (ret < 0)
20962306a36Sopenharmony_ci			goto err;
21062306a36Sopenharmony_ci		msb = ret;
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if (reg->lsb) {
21462306a36Sopenharmony_ci		ret = i2c_smbus_read_byte_data(isl29501->client, reg->lsb);
21562306a36Sopenharmony_ci		if (ret < 0)
21662306a36Sopenharmony_ci			goto err;
21762306a36Sopenharmony_ci		lsb = ret;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci	mutex_unlock(&isl29501->lock);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	*val = (msb << 8) + lsb;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	return 0;
22462306a36Sopenharmony_cierr:
22562306a36Sopenharmony_ci	mutex_unlock(&isl29501->lock);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	return ret;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic u32 isl29501_register_write(struct isl29501_private *isl29501,
23162306a36Sopenharmony_ci				   enum isl29501_register_name name,
23262306a36Sopenharmony_ci				   u32 value)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	const struct isl29501_register_desc *reg = &isl29501_registers[name];
23562306a36Sopenharmony_ci	int ret;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	if (!reg->msb && value > U8_MAX)
23862306a36Sopenharmony_ci		return -ERANGE;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (value > U16_MAX)
24162306a36Sopenharmony_ci		return -ERANGE;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	mutex_lock(&isl29501->lock);
24462306a36Sopenharmony_ci	if (reg->msb) {
24562306a36Sopenharmony_ci		ret = i2c_smbus_write_byte_data(isl29501->client,
24662306a36Sopenharmony_ci						reg->msb, value >> 8);
24762306a36Sopenharmony_ci		if (ret < 0)
24862306a36Sopenharmony_ci			goto err;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	ret = i2c_smbus_write_byte_data(isl29501->client, reg->lsb, value);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cierr:
25462306a36Sopenharmony_ci	mutex_unlock(&isl29501->lock);
25562306a36Sopenharmony_ci	return ret;
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic ssize_t isl29501_read_ext(struct iio_dev *indio_dev,
25962306a36Sopenharmony_ci				 uintptr_t private,
26062306a36Sopenharmony_ci				 const struct iio_chan_spec *chan,
26162306a36Sopenharmony_ci				 char *buf)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct isl29501_private *isl29501 = iio_priv(indio_dev);
26462306a36Sopenharmony_ci	enum isl29501_register_name reg = private;
26562306a36Sopenharmony_ci	int ret;
26662306a36Sopenharmony_ci	u32 value, gain, coeff, exp;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	switch (reg) {
26962306a36Sopenharmony_ci	case REG_GAIN:
27062306a36Sopenharmony_ci	case REG_GAIN_BIAS:
27162306a36Sopenharmony_ci		ret = isl29501_register_read(isl29501, reg, &gain);
27262306a36Sopenharmony_ci		if (ret < 0)
27362306a36Sopenharmony_ci			return ret;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci		value = gain;
27662306a36Sopenharmony_ci		break;
27762306a36Sopenharmony_ci	case REG_CALIB_PHASE_TEMP_A:
27862306a36Sopenharmony_ci	case REG_CALIB_PHASE_TEMP_B:
27962306a36Sopenharmony_ci	case REG_CALIB_PHASE_LIGHT_A:
28062306a36Sopenharmony_ci	case REG_CALIB_PHASE_LIGHT_B:
28162306a36Sopenharmony_ci		ret = isl29501_register_read(isl29501, REG_PHASE_EXP, &exp);
28262306a36Sopenharmony_ci		if (ret < 0)
28362306a36Sopenharmony_ci			return ret;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci		ret = isl29501_register_read(isl29501, reg, &coeff);
28662306a36Sopenharmony_ci		if (ret < 0)
28762306a36Sopenharmony_ci			return ret;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci		value = coeff << exp;
29062306a36Sopenharmony_ci		break;
29162306a36Sopenharmony_ci	default:
29262306a36Sopenharmony_ci		return -EINVAL;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	return sprintf(buf, "%u\n", value);
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic int isl29501_set_shadow_coeff(struct isl29501_private *isl29501,
29962306a36Sopenharmony_ci				     enum isl29501_register_name reg,
30062306a36Sopenharmony_ci				     unsigned int val)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	enum isl29501_correction_coeff coeff;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	switch (reg) {
30562306a36Sopenharmony_ci	case REG_CALIB_PHASE_TEMP_A:
30662306a36Sopenharmony_ci		coeff = COEFF_TEMP_A;
30762306a36Sopenharmony_ci		break;
30862306a36Sopenharmony_ci	case REG_CALIB_PHASE_TEMP_B:
30962306a36Sopenharmony_ci		coeff = COEFF_TEMP_B;
31062306a36Sopenharmony_ci		break;
31162306a36Sopenharmony_ci	case REG_CALIB_PHASE_LIGHT_A:
31262306a36Sopenharmony_ci		coeff = COEFF_LIGHT_A;
31362306a36Sopenharmony_ci		break;
31462306a36Sopenharmony_ci	case REG_CALIB_PHASE_LIGHT_B:
31562306a36Sopenharmony_ci		coeff = COEFF_LIGHT_B;
31662306a36Sopenharmony_ci		break;
31762306a36Sopenharmony_ci	default:
31862306a36Sopenharmony_ci		return -EINVAL;
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci	isl29501->shadow_coeffs[coeff] = val;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	return 0;
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic int isl29501_write_coeff(struct isl29501_private *isl29501,
32662306a36Sopenharmony_ci				enum isl29501_correction_coeff coeff,
32762306a36Sopenharmony_ci				int val)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	enum isl29501_register_name reg;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	switch (coeff) {
33262306a36Sopenharmony_ci	case COEFF_TEMP_A:
33362306a36Sopenharmony_ci		reg = REG_CALIB_PHASE_TEMP_A;
33462306a36Sopenharmony_ci		break;
33562306a36Sopenharmony_ci	case COEFF_TEMP_B:
33662306a36Sopenharmony_ci		reg = REG_CALIB_PHASE_TEMP_B;
33762306a36Sopenharmony_ci		break;
33862306a36Sopenharmony_ci	case COEFF_LIGHT_A:
33962306a36Sopenharmony_ci		reg = REG_CALIB_PHASE_LIGHT_A;
34062306a36Sopenharmony_ci		break;
34162306a36Sopenharmony_ci	case COEFF_LIGHT_B:
34262306a36Sopenharmony_ci		reg = REG_CALIB_PHASE_LIGHT_B;
34362306a36Sopenharmony_ci		break;
34462306a36Sopenharmony_ci	default:
34562306a36Sopenharmony_ci		return -EINVAL;
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	return isl29501_register_write(isl29501, reg, val);
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic unsigned int isl29501_find_corr_exp(unsigned int val,
35262306a36Sopenharmony_ci					   unsigned int max_exp,
35362306a36Sopenharmony_ci					   unsigned int max_mantissa)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	unsigned int exp = 1;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	/*
35862306a36Sopenharmony_ci	 * Correction coefficients are represented under
35962306a36Sopenharmony_ci	 * mantissa * 2^exponent form, where mantissa and exponent
36062306a36Sopenharmony_ci	 * are stored in two separate registers of the sensor.
36162306a36Sopenharmony_ci	 *
36262306a36Sopenharmony_ci	 * Compute and return the lowest exponent such as:
36362306a36Sopenharmony_ci	 *	     mantissa = value / 2^exponent
36462306a36Sopenharmony_ci	 *
36562306a36Sopenharmony_ci	 *  where mantissa < max_mantissa.
36662306a36Sopenharmony_ci	 */
36762306a36Sopenharmony_ci	if (val <= max_mantissa)
36862306a36Sopenharmony_ci		return 0;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	while ((val >> exp) > max_mantissa) {
37162306a36Sopenharmony_ci		exp++;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci		if (exp > max_exp)
37462306a36Sopenharmony_ci			return max_exp;
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	return exp;
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic ssize_t isl29501_write_ext(struct iio_dev *indio_dev,
38162306a36Sopenharmony_ci				  uintptr_t private,
38262306a36Sopenharmony_ci				  const struct iio_chan_spec *chan,
38362306a36Sopenharmony_ci				  const char *buf, size_t len)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	struct isl29501_private *isl29501 = iio_priv(indio_dev);
38662306a36Sopenharmony_ci	enum isl29501_register_name reg = private;
38762306a36Sopenharmony_ci	unsigned int val;
38862306a36Sopenharmony_ci	int max_exp = 0;
38962306a36Sopenharmony_ci	int ret;
39062306a36Sopenharmony_ci	int i;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	ret = kstrtouint(buf, 10, &val);
39362306a36Sopenharmony_ci	if (ret)
39462306a36Sopenharmony_ci		return ret;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	switch (reg) {
39762306a36Sopenharmony_ci	case REG_GAIN_BIAS:
39862306a36Sopenharmony_ci		if (val > U16_MAX)
39962306a36Sopenharmony_ci			return -ERANGE;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci		ret = isl29501_register_write(isl29501, reg, val);
40262306a36Sopenharmony_ci		if (ret < 0)
40362306a36Sopenharmony_ci			return ret;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci		break;
40662306a36Sopenharmony_ci	case REG_CALIB_PHASE_TEMP_A:
40762306a36Sopenharmony_ci	case REG_CALIB_PHASE_TEMP_B:
40862306a36Sopenharmony_ci	case REG_CALIB_PHASE_LIGHT_A:
40962306a36Sopenharmony_ci	case REG_CALIB_PHASE_LIGHT_B:
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci		if (val > (U8_MAX << ISL29501_MAX_EXP_VAL))
41262306a36Sopenharmony_ci			return -ERANGE;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci		/* Store the correction coefficient under its exact form. */
41562306a36Sopenharmony_ci		ret = isl29501_set_shadow_coeff(isl29501, reg, val);
41662306a36Sopenharmony_ci		if (ret < 0)
41762306a36Sopenharmony_ci			return ret;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci		/*
42062306a36Sopenharmony_ci		 * Find the highest exponent needed to represent
42162306a36Sopenharmony_ci		 * correction coefficients.
42262306a36Sopenharmony_ci		 */
42362306a36Sopenharmony_ci		for (i = 0; i < COEFF_MAX; i++) {
42462306a36Sopenharmony_ci			int corr;
42562306a36Sopenharmony_ci			int corr_exp;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci			corr = isl29501->shadow_coeffs[i];
42862306a36Sopenharmony_ci			corr_exp = isl29501_find_corr_exp(corr,
42962306a36Sopenharmony_ci							  ISL29501_MAX_EXP_VAL,
43062306a36Sopenharmony_ci							  U8_MAX / 2);
43162306a36Sopenharmony_ci			dev_dbg(&isl29501->client->dev,
43262306a36Sopenharmony_ci				"found exp of corr(%d) = %d\n", corr, corr_exp);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci			max_exp = max(max_exp, corr_exp);
43562306a36Sopenharmony_ci		}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci		/*
43862306a36Sopenharmony_ci		 * Represent every correction coefficient under
43962306a36Sopenharmony_ci		 * mantissa * 2^max_exponent form and force the
44062306a36Sopenharmony_ci		 * writing of those coefficients on the sensor.
44162306a36Sopenharmony_ci		 */
44262306a36Sopenharmony_ci		for (i = 0; i < COEFF_MAX; i++) {
44362306a36Sopenharmony_ci			int corr;
44462306a36Sopenharmony_ci			int mantissa;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci			corr = isl29501->shadow_coeffs[i];
44762306a36Sopenharmony_ci			if (!corr)
44862306a36Sopenharmony_ci				continue;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci			mantissa = corr >> max_exp;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci			ret = isl29501_write_coeff(isl29501, i, mantissa);
45362306a36Sopenharmony_ci			if (ret < 0)
45462306a36Sopenharmony_ci				return ret;
45562306a36Sopenharmony_ci		}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci		ret = isl29501_register_write(isl29501, REG_PHASE_EXP, max_exp);
45862306a36Sopenharmony_ci		if (ret < 0)
45962306a36Sopenharmony_ci			return ret;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci		break;
46262306a36Sopenharmony_ci	default:
46362306a36Sopenharmony_ci		return -EINVAL;
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	return len;
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci#define _ISL29501_EXT_INFO(_name, _ident) { \
47062306a36Sopenharmony_ci	.name = _name, \
47162306a36Sopenharmony_ci	.read = isl29501_read_ext, \
47262306a36Sopenharmony_ci	.write = isl29501_write_ext, \
47362306a36Sopenharmony_ci	.private = _ident, \
47462306a36Sopenharmony_ci	.shared = IIO_SEPARATE, \
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cistatic const struct iio_chan_spec_ext_info isl29501_ext_info[] = {
47862306a36Sopenharmony_ci	_ISL29501_EXT_INFO("agc_gain", REG_GAIN),
47962306a36Sopenharmony_ci	_ISL29501_EXT_INFO("agc_gain_bias", REG_GAIN_BIAS),
48062306a36Sopenharmony_ci	_ISL29501_EXT_INFO("calib_phase_temp_a", REG_CALIB_PHASE_TEMP_A),
48162306a36Sopenharmony_ci	_ISL29501_EXT_INFO("calib_phase_temp_b", REG_CALIB_PHASE_TEMP_B),
48262306a36Sopenharmony_ci	_ISL29501_EXT_INFO("calib_phase_light_a", REG_CALIB_PHASE_LIGHT_A),
48362306a36Sopenharmony_ci	_ISL29501_EXT_INFO("calib_phase_light_b", REG_CALIB_PHASE_LIGHT_B),
48462306a36Sopenharmony_ci	{ },
48562306a36Sopenharmony_ci};
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci#define ISL29501_DISTANCE_SCAN_INDEX 0
48862306a36Sopenharmony_ci#define ISL29501_TIMESTAMP_SCAN_INDEX 1
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cistatic const struct iio_chan_spec isl29501_channels[] = {
49162306a36Sopenharmony_ci	{
49262306a36Sopenharmony_ci		.type = IIO_PROXIMITY,
49362306a36Sopenharmony_ci		.scan_index = ISL29501_DISTANCE_SCAN_INDEX,
49462306a36Sopenharmony_ci		.info_mask_separate =
49562306a36Sopenharmony_ci			BIT(IIO_CHAN_INFO_RAW)   |
49662306a36Sopenharmony_ci			BIT(IIO_CHAN_INFO_SCALE) |
49762306a36Sopenharmony_ci			BIT(IIO_CHAN_INFO_CALIBBIAS),
49862306a36Sopenharmony_ci		.scan_type = {
49962306a36Sopenharmony_ci			.sign = 'u',
50062306a36Sopenharmony_ci			.realbits = 16,
50162306a36Sopenharmony_ci			.storagebits = 16,
50262306a36Sopenharmony_ci			.endianness = IIO_CPU,
50362306a36Sopenharmony_ci		},
50462306a36Sopenharmony_ci		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) |
50562306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_SAMP_FREQ),
50662306a36Sopenharmony_ci		.ext_info = isl29501_ext_info,
50762306a36Sopenharmony_ci	},
50862306a36Sopenharmony_ci	{
50962306a36Sopenharmony_ci		.type = IIO_PHASE,
51062306a36Sopenharmony_ci		.scan_index = -1,
51162306a36Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
51262306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_SCALE),
51362306a36Sopenharmony_ci	},
51462306a36Sopenharmony_ci	{
51562306a36Sopenharmony_ci		.type = IIO_CURRENT,
51662306a36Sopenharmony_ci		.scan_index = -1,
51762306a36Sopenharmony_ci		.output = 1,
51862306a36Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
51962306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_SCALE),
52062306a36Sopenharmony_ci	},
52162306a36Sopenharmony_ci	{
52262306a36Sopenharmony_ci		.type = IIO_TEMP,
52362306a36Sopenharmony_ci		.scan_index = -1,
52462306a36Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
52562306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_SCALE)     |
52662306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBBIAS),
52762306a36Sopenharmony_ci	},
52862306a36Sopenharmony_ci	{
52962306a36Sopenharmony_ci		.type = IIO_INTENSITY,
53062306a36Sopenharmony_ci		.scan_index = -1,
53162306a36Sopenharmony_ci		.modified = 1,
53262306a36Sopenharmony_ci		.channel2 = IIO_MOD_LIGHT_CLEAR,
53362306a36Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
53462306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_SCALE),
53562306a36Sopenharmony_ci	},
53662306a36Sopenharmony_ci	IIO_CHAN_SOFT_TIMESTAMP(ISL29501_TIMESTAMP_SCAN_INDEX),
53762306a36Sopenharmony_ci};
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_cistatic int isl29501_reset_registers(struct isl29501_private *isl29501)
54062306a36Sopenharmony_ci{
54162306a36Sopenharmony_ci	int ret;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	ret = i2c_smbus_write_byte_data(isl29501->client,
54462306a36Sopenharmony_ci					ISL29501_COMMAND_REGISTER,
54562306a36Sopenharmony_ci					ISL29501_RESET_ALL_REGISTERS);
54662306a36Sopenharmony_ci	if (ret < 0) {
54762306a36Sopenharmony_ci		dev_err(&isl29501->client->dev,
54862306a36Sopenharmony_ci			"cannot reset registers %d\n", ret);
54962306a36Sopenharmony_ci		return ret;
55062306a36Sopenharmony_ci	}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	ret = i2c_smbus_write_byte_data(isl29501->client,
55362306a36Sopenharmony_ci					ISL29501_COMMAND_REGISTER,
55462306a36Sopenharmony_ci					ISL29501_RESET_INT_SM);
55562306a36Sopenharmony_ci	if (ret < 0)
55662306a36Sopenharmony_ci		dev_err(&isl29501->client->dev,
55762306a36Sopenharmony_ci			"cannot reset state machine %d\n", ret);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	return ret;
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic int isl29501_begin_acquisition(struct isl29501_private *isl29501)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	int ret;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	ret = i2c_smbus_write_byte_data(isl29501->client,
56762306a36Sopenharmony_ci					ISL29501_COMMAND_REGISTER,
56862306a36Sopenharmony_ci					ISL29501_EMUL_SAMPLE_START_PIN);
56962306a36Sopenharmony_ci	if (ret < 0)
57062306a36Sopenharmony_ci		dev_err(&isl29501->client->dev,
57162306a36Sopenharmony_ci			"cannot begin acquisition %d\n", ret);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	return ret;
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_cistatic IIO_CONST_ATTR_INT_TIME_AVAIL(ISL29501_INT_TIME_AVAILABLE);
57762306a36Sopenharmony_cistatic IIO_CONST_ATTR(out_current_scale_available,
57862306a36Sopenharmony_ci		      ISL29501_CURRENT_SCALE_AVAILABLE);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic struct attribute *isl29501_attributes[] = {
58162306a36Sopenharmony_ci	&iio_const_attr_integration_time_available.dev_attr.attr,
58262306a36Sopenharmony_ci	&iio_const_attr_out_current_scale_available.dev_attr.attr,
58362306a36Sopenharmony_ci	NULL
58462306a36Sopenharmony_ci};
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_cistatic const struct attribute_group isl29501_attribute_group = {
58762306a36Sopenharmony_ci	.attrs = isl29501_attributes,
58862306a36Sopenharmony_ci};
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_cistatic const int isl29501_current_scale_table[][2] = {
59162306a36Sopenharmony_ci	{0, 3900}, {0, 7800}, {0, 11800}, {0, 15700},
59262306a36Sopenharmony_ci	{0, 19600}, {0, 23500}, {0, 27500}, {0, 31400},
59362306a36Sopenharmony_ci	{0, 35200}, {0, 39200}, {0, 43100}, {0, 47100},
59462306a36Sopenharmony_ci	{0, 51000}, {0, 54900}, {0, 58800},
59562306a36Sopenharmony_ci};
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_cistatic const int isl29501_int_time[][2] = {
59862306a36Sopenharmony_ci	{0, 70},    /* 0.07 ms */
59962306a36Sopenharmony_ci	{0, 140},   /* 0.14 ms */
60062306a36Sopenharmony_ci	{0, 280},   /* 0.28 ms */
60162306a36Sopenharmony_ci	{0, 570},   /* 0.57 ms */
60262306a36Sopenharmony_ci	{0, 1140},  /* 1.14 ms */
60362306a36Sopenharmony_ci	{0, 2280},  /* 2.28 ms */
60462306a36Sopenharmony_ci	{0, 4550},  /* 4.55 ms */
60562306a36Sopenharmony_ci	{0, 9100},  /* 9.11 ms */
60662306a36Sopenharmony_ci	{0, 18200}, /* 18.2 ms */
60762306a36Sopenharmony_ci	{0, 36400}, /* 36.4 ms */
60862306a36Sopenharmony_ci	{0, 72810}, /* 72.81 ms */
60962306a36Sopenharmony_ci	{0, 145610} /* 145.28 ms */
61062306a36Sopenharmony_ci};
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic int isl29501_get_raw(struct isl29501_private *isl29501,
61362306a36Sopenharmony_ci			    const struct iio_chan_spec *chan,
61462306a36Sopenharmony_ci			    int *raw)
61562306a36Sopenharmony_ci{
61662306a36Sopenharmony_ci	int ret;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	switch (chan->type) {
61962306a36Sopenharmony_ci	case IIO_PROXIMITY:
62062306a36Sopenharmony_ci		ret = isl29501_register_read(isl29501, REG_DISTANCE, raw);
62162306a36Sopenharmony_ci		if (ret < 0)
62262306a36Sopenharmony_ci			return ret;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci		return IIO_VAL_INT;
62562306a36Sopenharmony_ci	case IIO_INTENSITY:
62662306a36Sopenharmony_ci		ret = isl29501_register_read(isl29501,
62762306a36Sopenharmony_ci					     REG_AMBIENT_LIGHT,
62862306a36Sopenharmony_ci					     raw);
62962306a36Sopenharmony_ci		if (ret < 0)
63062306a36Sopenharmony_ci			return ret;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci		return IIO_VAL_INT;
63362306a36Sopenharmony_ci	case IIO_PHASE:
63462306a36Sopenharmony_ci		ret = isl29501_register_read(isl29501, REG_PHASE, raw);
63562306a36Sopenharmony_ci		if (ret < 0)
63662306a36Sopenharmony_ci			return ret;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci		return IIO_VAL_INT;
63962306a36Sopenharmony_ci	case IIO_CURRENT:
64062306a36Sopenharmony_ci		ret = isl29501_register_read(isl29501, REG_EMITTER_DAC, raw);
64162306a36Sopenharmony_ci		if (ret < 0)
64262306a36Sopenharmony_ci			return ret;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci		return IIO_VAL_INT;
64562306a36Sopenharmony_ci	case IIO_TEMP:
64662306a36Sopenharmony_ci		ret = isl29501_register_read(isl29501, REG_TEMPERATURE, raw);
64762306a36Sopenharmony_ci		if (ret < 0)
64862306a36Sopenharmony_ci			return ret;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci		return IIO_VAL_INT;
65162306a36Sopenharmony_ci	default:
65262306a36Sopenharmony_ci		return -EINVAL;
65362306a36Sopenharmony_ci	}
65462306a36Sopenharmony_ci}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_cistatic int isl29501_get_scale(struct isl29501_private *isl29501,
65762306a36Sopenharmony_ci			      const struct iio_chan_spec *chan,
65862306a36Sopenharmony_ci			      int *val, int *val2)
65962306a36Sopenharmony_ci{
66062306a36Sopenharmony_ci	int ret;
66162306a36Sopenharmony_ci	u32 current_scale;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	switch (chan->type) {
66462306a36Sopenharmony_ci	case IIO_PROXIMITY:
66562306a36Sopenharmony_ci		/* distance = raw_distance * 33.31 / 65536 (m) */
66662306a36Sopenharmony_ci		*val = 3331;
66762306a36Sopenharmony_ci		*val2 = 6553600;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci		return IIO_VAL_FRACTIONAL;
67062306a36Sopenharmony_ci	case IIO_PHASE:
67162306a36Sopenharmony_ci		/* phase = raw_phase * 2pi / 65536 (rad) */
67262306a36Sopenharmony_ci		*val = 0;
67362306a36Sopenharmony_ci		*val2 = 95874;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci		return IIO_VAL_INT_PLUS_NANO;
67662306a36Sopenharmony_ci	case IIO_INTENSITY:
67762306a36Sopenharmony_ci		/* light = raw_light * 35 / 10000 (mA) */
67862306a36Sopenharmony_ci		*val = 35;
67962306a36Sopenharmony_ci		*val2 = 10000;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci		return IIO_VAL_FRACTIONAL;
68262306a36Sopenharmony_ci	case IIO_CURRENT:
68362306a36Sopenharmony_ci		ret = isl29501_register_read(isl29501,
68462306a36Sopenharmony_ci					     REG_DRIVER_RANGE,
68562306a36Sopenharmony_ci					     &current_scale);
68662306a36Sopenharmony_ci		if (ret < 0)
68762306a36Sopenharmony_ci			return ret;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci		if (current_scale > ARRAY_SIZE(isl29501_current_scale_table))
69062306a36Sopenharmony_ci			return -EINVAL;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci		if (!current_scale) {
69362306a36Sopenharmony_ci			*val = 0;
69462306a36Sopenharmony_ci			*val2 = 0;
69562306a36Sopenharmony_ci			return IIO_VAL_INT;
69662306a36Sopenharmony_ci		}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci		*val = isl29501_current_scale_table[current_scale - 1][0];
69962306a36Sopenharmony_ci		*val2 = isl29501_current_scale_table[current_scale - 1][1];
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci		return IIO_VAL_INT_PLUS_MICRO;
70262306a36Sopenharmony_ci	case IIO_TEMP:
70362306a36Sopenharmony_ci		/* temperature = raw_temperature * 125 / 100000 (milli °C) */
70462306a36Sopenharmony_ci		*val = 125;
70562306a36Sopenharmony_ci		*val2 = 100000;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci		return IIO_VAL_FRACTIONAL;
70862306a36Sopenharmony_ci	default:
70962306a36Sopenharmony_ci		return -EINVAL;
71062306a36Sopenharmony_ci	}
71162306a36Sopenharmony_ci}
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_cistatic int isl29501_get_calibbias(struct isl29501_private *isl29501,
71462306a36Sopenharmony_ci				  const struct iio_chan_spec *chan,
71562306a36Sopenharmony_ci				  int *bias)
71662306a36Sopenharmony_ci{
71762306a36Sopenharmony_ci	switch (chan->type) {
71862306a36Sopenharmony_ci	case IIO_PROXIMITY:
71962306a36Sopenharmony_ci		return isl29501_register_read(isl29501,
72062306a36Sopenharmony_ci					      REG_DISTANCE_BIAS,
72162306a36Sopenharmony_ci					      bias);
72262306a36Sopenharmony_ci	case IIO_TEMP:
72362306a36Sopenharmony_ci		return isl29501_register_read(isl29501,
72462306a36Sopenharmony_ci					      REG_TEMPERATURE_BIAS,
72562306a36Sopenharmony_ci					      bias);
72662306a36Sopenharmony_ci	default:
72762306a36Sopenharmony_ci		return -EINVAL;
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_cistatic int isl29501_get_inttime(struct isl29501_private *isl29501,
73262306a36Sopenharmony_ci				int *val, int *val2)
73362306a36Sopenharmony_ci{
73462306a36Sopenharmony_ci	int ret;
73562306a36Sopenharmony_ci	u32 inttime;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	ret = isl29501_register_read(isl29501, REG_INT_TIME, &inttime);
73862306a36Sopenharmony_ci	if (ret < 0)
73962306a36Sopenharmony_ci		return ret;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	if (inttime >= ARRAY_SIZE(isl29501_int_time))
74262306a36Sopenharmony_ci		return -EINVAL;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	*val = isl29501_int_time[inttime][0];
74562306a36Sopenharmony_ci	*val2 = isl29501_int_time[inttime][1];
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	return IIO_VAL_INT_PLUS_MICRO;
74862306a36Sopenharmony_ci}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_cistatic int isl29501_get_freq(struct isl29501_private *isl29501,
75162306a36Sopenharmony_ci			     int *val, int *val2)
75262306a36Sopenharmony_ci{
75362306a36Sopenharmony_ci	int ret;
75462306a36Sopenharmony_ci	int sample_time;
75562306a36Sopenharmony_ci	unsigned long long freq;
75662306a36Sopenharmony_ci	u32 temp;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	ret = isl29501_register_read(isl29501, REG_SAMPLE_TIME, &sample_time);
75962306a36Sopenharmony_ci	if (ret < 0)
76062306a36Sopenharmony_ci		return ret;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	/* freq = 1 / (0.000450 * (sample_time + 1) * 10^-6) */
76362306a36Sopenharmony_ci	freq = 1000000ULL * 1000000ULL;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	do_div(freq, 450 * (sample_time + 1));
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	temp = do_div(freq, 1000000);
76862306a36Sopenharmony_ci	*val = freq;
76962306a36Sopenharmony_ci	*val2 = temp;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	return IIO_VAL_INT_PLUS_MICRO;
77262306a36Sopenharmony_ci}
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_cistatic int isl29501_read_raw(struct iio_dev *indio_dev,
77562306a36Sopenharmony_ci			     struct iio_chan_spec const *chan, int *val,
77662306a36Sopenharmony_ci			     int *val2, long mask)
77762306a36Sopenharmony_ci{
77862306a36Sopenharmony_ci	struct isl29501_private *isl29501 = iio_priv(indio_dev);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	switch (mask) {
78162306a36Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
78262306a36Sopenharmony_ci		return isl29501_get_raw(isl29501, chan, val);
78362306a36Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
78462306a36Sopenharmony_ci		return isl29501_get_scale(isl29501, chan, val, val2);
78562306a36Sopenharmony_ci	case IIO_CHAN_INFO_INT_TIME:
78662306a36Sopenharmony_ci		return isl29501_get_inttime(isl29501, val, val2);
78762306a36Sopenharmony_ci	case IIO_CHAN_INFO_SAMP_FREQ:
78862306a36Sopenharmony_ci		return isl29501_get_freq(isl29501, val, val2);
78962306a36Sopenharmony_ci	case IIO_CHAN_INFO_CALIBBIAS:
79062306a36Sopenharmony_ci		return isl29501_get_calibbias(isl29501, chan, val);
79162306a36Sopenharmony_ci	default:
79262306a36Sopenharmony_ci		return -EINVAL;
79362306a36Sopenharmony_ci	}
79462306a36Sopenharmony_ci}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_cistatic int isl29501_set_raw(struct isl29501_private *isl29501,
79762306a36Sopenharmony_ci			    const struct iio_chan_spec *chan,
79862306a36Sopenharmony_ci			    int raw)
79962306a36Sopenharmony_ci{
80062306a36Sopenharmony_ci	switch (chan->type) {
80162306a36Sopenharmony_ci	case IIO_CURRENT:
80262306a36Sopenharmony_ci		return isl29501_register_write(isl29501, REG_EMITTER_DAC, raw);
80362306a36Sopenharmony_ci	default:
80462306a36Sopenharmony_ci		return -EINVAL;
80562306a36Sopenharmony_ci	}
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_cistatic int isl29501_set_inttime(struct isl29501_private *isl29501,
80962306a36Sopenharmony_ci				int val, int val2)
81062306a36Sopenharmony_ci{
81162306a36Sopenharmony_ci	int i;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(isl29501_int_time); i++) {
81462306a36Sopenharmony_ci		if (isl29501_int_time[i][0] == val &&
81562306a36Sopenharmony_ci		    isl29501_int_time[i][1] == val2) {
81662306a36Sopenharmony_ci			return isl29501_register_write(isl29501,
81762306a36Sopenharmony_ci						       REG_INT_TIME,
81862306a36Sopenharmony_ci						       i);
81962306a36Sopenharmony_ci		}
82062306a36Sopenharmony_ci	}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	return -EINVAL;
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_cistatic int isl29501_set_scale(struct isl29501_private *isl29501,
82662306a36Sopenharmony_ci			      const struct iio_chan_spec *chan,
82762306a36Sopenharmony_ci			      int val, int val2)
82862306a36Sopenharmony_ci{
82962306a36Sopenharmony_ci	int i;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	if (chan->type != IIO_CURRENT)
83262306a36Sopenharmony_ci		return -EINVAL;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(isl29501_current_scale_table); i++) {
83562306a36Sopenharmony_ci		if (isl29501_current_scale_table[i][0] == val &&
83662306a36Sopenharmony_ci		    isl29501_current_scale_table[i][1] == val2) {
83762306a36Sopenharmony_ci			return isl29501_register_write(isl29501,
83862306a36Sopenharmony_ci						       REG_DRIVER_RANGE,
83962306a36Sopenharmony_ci						       i + 1);
84062306a36Sopenharmony_ci		}
84162306a36Sopenharmony_ci	}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	return -EINVAL;
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_cistatic int isl29501_set_calibbias(struct isl29501_private *isl29501,
84762306a36Sopenharmony_ci				  const struct iio_chan_spec *chan,
84862306a36Sopenharmony_ci				  int bias)
84962306a36Sopenharmony_ci{
85062306a36Sopenharmony_ci	switch (chan->type) {
85162306a36Sopenharmony_ci	case IIO_PROXIMITY:
85262306a36Sopenharmony_ci		return isl29501_register_write(isl29501,
85362306a36Sopenharmony_ci					      REG_DISTANCE_BIAS,
85462306a36Sopenharmony_ci					      bias);
85562306a36Sopenharmony_ci	case IIO_TEMP:
85662306a36Sopenharmony_ci		return isl29501_register_write(isl29501,
85762306a36Sopenharmony_ci					       REG_TEMPERATURE_BIAS,
85862306a36Sopenharmony_ci					       bias);
85962306a36Sopenharmony_ci	default:
86062306a36Sopenharmony_ci		return -EINVAL;
86162306a36Sopenharmony_ci	}
86262306a36Sopenharmony_ci}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_cistatic int isl29501_set_freq(struct isl29501_private *isl29501,
86562306a36Sopenharmony_ci			     int val, int val2)
86662306a36Sopenharmony_ci{
86762306a36Sopenharmony_ci	int freq;
86862306a36Sopenharmony_ci	unsigned long long sample_time;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	/* sample_freq = 1 / (0.000450 * (sample_time + 1) * 10^-6) */
87162306a36Sopenharmony_ci	freq = val * 1000000 + val2 % 1000000;
87262306a36Sopenharmony_ci	sample_time = 2222ULL * 1000000ULL;
87362306a36Sopenharmony_ci	do_div(sample_time, freq);
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	sample_time -= 1;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	if (sample_time > 255)
87862306a36Sopenharmony_ci		return -ERANGE;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	return isl29501_register_write(isl29501, REG_SAMPLE_TIME, sample_time);
88162306a36Sopenharmony_ci}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_cistatic int isl29501_write_raw(struct iio_dev *indio_dev,
88462306a36Sopenharmony_ci			      struct iio_chan_spec const *chan,
88562306a36Sopenharmony_ci			      int val, int val2, long mask)
88662306a36Sopenharmony_ci{
88762306a36Sopenharmony_ci	struct isl29501_private *isl29501 = iio_priv(indio_dev);
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	switch (mask) {
89062306a36Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
89162306a36Sopenharmony_ci		return isl29501_set_raw(isl29501, chan, val);
89262306a36Sopenharmony_ci	case IIO_CHAN_INFO_INT_TIME:
89362306a36Sopenharmony_ci		return isl29501_set_inttime(isl29501, val, val2);
89462306a36Sopenharmony_ci	case IIO_CHAN_INFO_SAMP_FREQ:
89562306a36Sopenharmony_ci		return isl29501_set_freq(isl29501, val, val2);
89662306a36Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
89762306a36Sopenharmony_ci		return isl29501_set_scale(isl29501, chan, val, val2);
89862306a36Sopenharmony_ci	case IIO_CHAN_INFO_CALIBBIAS:
89962306a36Sopenharmony_ci		return isl29501_set_calibbias(isl29501, chan, val);
90062306a36Sopenharmony_ci	default:
90162306a36Sopenharmony_ci		return -EINVAL;
90262306a36Sopenharmony_ci	}
90362306a36Sopenharmony_ci}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_cistatic const struct iio_info isl29501_info = {
90662306a36Sopenharmony_ci	.read_raw = &isl29501_read_raw,
90762306a36Sopenharmony_ci	.write_raw = &isl29501_write_raw,
90862306a36Sopenharmony_ci	.attrs = &isl29501_attribute_group,
90962306a36Sopenharmony_ci};
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_cistatic int isl29501_init_chip(struct isl29501_private *isl29501)
91262306a36Sopenharmony_ci{
91362306a36Sopenharmony_ci	int ret;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	ret = i2c_smbus_read_byte_data(isl29501->client, ISL29501_DEVICE_ID);
91662306a36Sopenharmony_ci	if (ret < 0) {
91762306a36Sopenharmony_ci		dev_err(&isl29501->client->dev, "Error reading device id\n");
91862306a36Sopenharmony_ci		return ret;
91962306a36Sopenharmony_ci	}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	if (ret != ISL29501_ID) {
92262306a36Sopenharmony_ci		dev_err(&isl29501->client->dev,
92362306a36Sopenharmony_ci			"Wrong chip id, got %x expected %x\n",
92462306a36Sopenharmony_ci			ret, ISL29501_DEVICE_ID);
92562306a36Sopenharmony_ci		return -ENODEV;
92662306a36Sopenharmony_ci	}
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	ret = isl29501_reset_registers(isl29501);
92962306a36Sopenharmony_ci	if (ret < 0)
93062306a36Sopenharmony_ci		return ret;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	return isl29501_begin_acquisition(isl29501);
93362306a36Sopenharmony_ci}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_cistatic irqreturn_t isl29501_trigger_handler(int irq, void *p)
93662306a36Sopenharmony_ci{
93762306a36Sopenharmony_ci	struct iio_poll_func *pf = p;
93862306a36Sopenharmony_ci	struct iio_dev *indio_dev = pf->indio_dev;
93962306a36Sopenharmony_ci	struct isl29501_private *isl29501 = iio_priv(indio_dev);
94062306a36Sopenharmony_ci	const unsigned long *active_mask = indio_dev->active_scan_mask;
94162306a36Sopenharmony_ci	u32 buffer[4] __aligned(8) = {}; /* 1x16-bit + naturally aligned ts */
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	if (test_bit(ISL29501_DISTANCE_SCAN_INDEX, active_mask))
94462306a36Sopenharmony_ci		isl29501_register_read(isl29501, REG_DISTANCE, buffer);
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	iio_push_to_buffers_with_timestamp(indio_dev, buffer, pf->timestamp);
94762306a36Sopenharmony_ci	iio_trigger_notify_done(indio_dev->trig);
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	return IRQ_HANDLED;
95062306a36Sopenharmony_ci}
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_cistatic int isl29501_probe(struct i2c_client *client)
95362306a36Sopenharmony_ci{
95462306a36Sopenharmony_ci	struct iio_dev *indio_dev;
95562306a36Sopenharmony_ci	struct isl29501_private *isl29501;
95662306a36Sopenharmony_ci	int ret;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*isl29501));
95962306a36Sopenharmony_ci	if (!indio_dev)
96062306a36Sopenharmony_ci		return -ENOMEM;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	isl29501 = iio_priv(indio_dev);
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	i2c_set_clientdata(client, indio_dev);
96562306a36Sopenharmony_ci	isl29501->client = client;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	mutex_init(&isl29501->lock);
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	ret = isl29501_init_chip(isl29501);
97062306a36Sopenharmony_ci	if (ret < 0)
97162306a36Sopenharmony_ci		return ret;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
97462306a36Sopenharmony_ci	indio_dev->channels = isl29501_channels;
97562306a36Sopenharmony_ci	indio_dev->num_channels = ARRAY_SIZE(isl29501_channels);
97662306a36Sopenharmony_ci	indio_dev->name = client->name;
97762306a36Sopenharmony_ci	indio_dev->info = &isl29501_info;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev,
98062306a36Sopenharmony_ci					      iio_pollfunc_store_time,
98162306a36Sopenharmony_ci					      isl29501_trigger_handler,
98262306a36Sopenharmony_ci					      NULL);
98362306a36Sopenharmony_ci	if (ret < 0) {
98462306a36Sopenharmony_ci		dev_err(&client->dev, "unable to setup iio triggered buffer\n");
98562306a36Sopenharmony_ci		return ret;
98662306a36Sopenharmony_ci	}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	return devm_iio_device_register(&client->dev, indio_dev);
98962306a36Sopenharmony_ci}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_cistatic const struct i2c_device_id isl29501_id[] = {
99262306a36Sopenharmony_ci	{"isl29501", 0},
99362306a36Sopenharmony_ci	{}
99462306a36Sopenharmony_ci};
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, isl29501_id);
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci#if defined(CONFIG_OF)
99962306a36Sopenharmony_cistatic const struct of_device_id isl29501_i2c_matches[] = {
100062306a36Sopenharmony_ci	{ .compatible = "renesas,isl29501" },
100162306a36Sopenharmony_ci	{ }
100262306a36Sopenharmony_ci};
100362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, isl29501_i2c_matches);
100462306a36Sopenharmony_ci#endif
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_cistatic struct i2c_driver isl29501_driver = {
100762306a36Sopenharmony_ci	.driver = {
100862306a36Sopenharmony_ci		.name	= "isl29501",
100962306a36Sopenharmony_ci	},
101062306a36Sopenharmony_ci	.id_table	= isl29501_id,
101162306a36Sopenharmony_ci	.probe		= isl29501_probe,
101262306a36Sopenharmony_ci};
101362306a36Sopenharmony_cimodule_i2c_driver(isl29501_driver);
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ciMODULE_AUTHOR("Mathieu Othacehe <m.othacehe@gmail.com>");
101662306a36Sopenharmony_ciMODULE_DESCRIPTION("ISL29501 Time of Flight sensor driver");
101762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
1018