162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * BU27034 ROHM Ambient Light Sensor
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2023, ROHM Semiconductor.
662306a36Sopenharmony_ci * https://fscdn.rohm.com/en/products/databook/datasheet/ic/sensor/light/bu27034nuc-e.pdf
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/bitfield.h>
1062306a36Sopenharmony_ci#include <linux/bits.h>
1162306a36Sopenharmony_ci#include <linux/device.h>
1262306a36Sopenharmony_ci#include <linux/i2c.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/property.h>
1562306a36Sopenharmony_ci#include <linux/regmap.h>
1662306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
1762306a36Sopenharmony_ci#include <linux/units.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/iio/buffer.h>
2062306a36Sopenharmony_ci#include <linux/iio/iio.h>
2162306a36Sopenharmony_ci#include <linux/iio/iio-gts-helper.h>
2262306a36Sopenharmony_ci#include <linux/iio/kfifo_buf.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define BU27034_REG_SYSTEM_CONTROL	0x40
2562306a36Sopenharmony_ci#define BU27034_MASK_SW_RESET		BIT(7)
2662306a36Sopenharmony_ci#define BU27034_MASK_PART_ID		GENMASK(5, 0)
2762306a36Sopenharmony_ci#define BU27034_ID			0x19
2862306a36Sopenharmony_ci#define BU27034_REG_MODE_CONTROL1	0x41
2962306a36Sopenharmony_ci#define BU27034_MASK_MEAS_MODE		GENMASK(2, 0)
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define BU27034_REG_MODE_CONTROL2	0x42
3262306a36Sopenharmony_ci#define BU27034_MASK_D01_GAIN		GENMASK(7, 3)
3362306a36Sopenharmony_ci#define BU27034_MASK_D2_GAIN_HI		GENMASK(7, 6)
3462306a36Sopenharmony_ci#define BU27034_MASK_D2_GAIN_LO		GENMASK(2, 0)
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define BU27034_REG_MODE_CONTROL3	0x43
3762306a36Sopenharmony_ci#define BU27034_REG_MODE_CONTROL4	0x44
3862306a36Sopenharmony_ci#define BU27034_MASK_MEAS_EN		BIT(0)
3962306a36Sopenharmony_ci#define BU27034_MASK_VALID		BIT(7)
4062306a36Sopenharmony_ci#define BU27034_REG_DATA0_LO		0x50
4162306a36Sopenharmony_ci#define BU27034_REG_DATA1_LO		0x52
4262306a36Sopenharmony_ci#define BU27034_REG_DATA2_LO		0x54
4362306a36Sopenharmony_ci#define BU27034_REG_DATA2_HI		0x55
4462306a36Sopenharmony_ci#define BU27034_REG_MANUFACTURER_ID	0x92
4562306a36Sopenharmony_ci#define BU27034_REG_MAX BU27034_REG_MANUFACTURER_ID
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/*
4862306a36Sopenharmony_ci * The BU27034 does not have interrupt to trigger the data read when a
4962306a36Sopenharmony_ci * measurement has finished. Hence we poll the VALID bit in a thread. We will
5062306a36Sopenharmony_ci * try to wake the thread BU27034_MEAS_WAIT_PREMATURE_MS milliseconds before
5162306a36Sopenharmony_ci * the expected sampling time to prevent the drifting.
5262306a36Sopenharmony_ci *
5362306a36Sopenharmony_ci * If we constantly wake up a bit too late we would eventually skip a sample.
5462306a36Sopenharmony_ci * And because the sleep can't wake up _exactly_ at given time this would be
5562306a36Sopenharmony_ci * inevitable even if the sensor clock would be perfectly phase-locked to CPU
5662306a36Sopenharmony_ci * clock - which we can't say is the case.
5762306a36Sopenharmony_ci *
5862306a36Sopenharmony_ci * This is still fragile. No matter how big advance do we have, we will still
5962306a36Sopenharmony_ci * risk of losing a sample because things can in a rainy-day scenario be
6062306a36Sopenharmony_ci * delayed a lot. Yet, more we reserve the time for polling, more we also lose
6162306a36Sopenharmony_ci * the performance by spending cycles polling the register. So, selecting this
6262306a36Sopenharmony_ci * value is a balancing dance between severity of wasting CPU time and severity
6362306a36Sopenharmony_ci * of losing samples.
6462306a36Sopenharmony_ci *
6562306a36Sopenharmony_ci * In most cases losing the samples is not _that_ crucial because light levels
6662306a36Sopenharmony_ci * tend to change slowly.
6762306a36Sopenharmony_ci *
6862306a36Sopenharmony_ci * Other option that was pointed to me would be always sleeping 1/2 of the
6962306a36Sopenharmony_ci * measurement time, checking the VALID bit and just sleeping again if the bit
7062306a36Sopenharmony_ci * was not set. That should be pretty tolerant against missing samples due to
7162306a36Sopenharmony_ci * the scheduling delays while also not wasting much of cycles for polling.
7262306a36Sopenharmony_ci * Downside is that the time-stamps would be very inaccurate as the wake-up
7362306a36Sopenharmony_ci * would not really be tied to the sensor toggling the valid bit. This would also
7462306a36Sopenharmony_ci * result 'jumps' in the time-stamps when the delay drifted so that wake-up was
7562306a36Sopenharmony_ci * performed during the consecutive wake-ups (Or, when sensor and CPU clocks
7662306a36Sopenharmony_ci * were very different and scheduling the wake-ups was very close to given
7762306a36Sopenharmony_ci * timeout - and when the time-outs were very close to the actual sensor
7862306a36Sopenharmony_ci * sampling, Eg. once in a blue moon, two consecutive time-outs would occur
7962306a36Sopenharmony_ci * without having a sample ready).
8062306a36Sopenharmony_ci */
8162306a36Sopenharmony_ci#define BU27034_MEAS_WAIT_PREMATURE_MS	5
8262306a36Sopenharmony_ci#define BU27034_DATA_WAIT_TIME_US	1000
8362306a36Sopenharmony_ci#define BU27034_TOTAL_DATA_WAIT_TIME_US (BU27034_MEAS_WAIT_PREMATURE_MS * 1000)
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci#define BU27034_RETRY_LIMIT 18
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cienum {
8862306a36Sopenharmony_ci	BU27034_CHAN_ALS,
8962306a36Sopenharmony_ci	BU27034_CHAN_DATA0,
9062306a36Sopenharmony_ci	BU27034_CHAN_DATA1,
9162306a36Sopenharmony_ci	BU27034_CHAN_DATA2,
9262306a36Sopenharmony_ci	BU27034_NUM_CHANS
9362306a36Sopenharmony_ci};
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic const unsigned long bu27034_scan_masks[] = {
9662306a36Sopenharmony_ci	GENMASK(BU27034_CHAN_DATA2, BU27034_CHAN_ALS), 0
9762306a36Sopenharmony_ci};
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/*
10062306a36Sopenharmony_ci * Available scales with gain 1x - 4096x, timings 55, 100, 200, 400 mS
10162306a36Sopenharmony_ci * Time impacts to gain: 1x, 2x, 4x, 8x.
10262306a36Sopenharmony_ci *
10362306a36Sopenharmony_ci * => Max total gain is HWGAIN * gain by integration time (8 * 4096) = 32768
10462306a36Sopenharmony_ci *
10562306a36Sopenharmony_ci * Using NANO precision for scale we must use scale 64x corresponding gain 1x
10662306a36Sopenharmony_ci * to avoid precision loss. (32x would result scale 976 562.5(nanos).
10762306a36Sopenharmony_ci */
10862306a36Sopenharmony_ci#define BU27034_SCALE_1X	64
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci/* See the data sheet for the "Gain Setting" table */
11162306a36Sopenharmony_ci#define BU27034_GSEL_1X		0x00 /* 00000 */
11262306a36Sopenharmony_ci#define BU27034_GSEL_4X		0x08 /* 01000 */
11362306a36Sopenharmony_ci#define BU27034_GSEL_16X	0x0a /* 01010 */
11462306a36Sopenharmony_ci#define BU27034_GSEL_32X	0x0b /* 01011 */
11562306a36Sopenharmony_ci#define BU27034_GSEL_64X	0x0c /* 01100 */
11662306a36Sopenharmony_ci#define BU27034_GSEL_256X	0x18 /* 11000 */
11762306a36Sopenharmony_ci#define BU27034_GSEL_512X	0x19 /* 11001 */
11862306a36Sopenharmony_ci#define BU27034_GSEL_1024X	0x1a /* 11010 */
11962306a36Sopenharmony_ci#define BU27034_GSEL_2048X	0x1b /* 11011 */
12062306a36Sopenharmony_ci#define BU27034_GSEL_4096X	0x1c /* 11100 */
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci/* Available gain settings */
12362306a36Sopenharmony_cistatic const struct iio_gain_sel_pair bu27034_gains[] = {
12462306a36Sopenharmony_ci	GAIN_SCALE_GAIN(1, BU27034_GSEL_1X),
12562306a36Sopenharmony_ci	GAIN_SCALE_GAIN(4, BU27034_GSEL_4X),
12662306a36Sopenharmony_ci	GAIN_SCALE_GAIN(16, BU27034_GSEL_16X),
12762306a36Sopenharmony_ci	GAIN_SCALE_GAIN(32, BU27034_GSEL_32X),
12862306a36Sopenharmony_ci	GAIN_SCALE_GAIN(64, BU27034_GSEL_64X),
12962306a36Sopenharmony_ci	GAIN_SCALE_GAIN(256, BU27034_GSEL_256X),
13062306a36Sopenharmony_ci	GAIN_SCALE_GAIN(512, BU27034_GSEL_512X),
13162306a36Sopenharmony_ci	GAIN_SCALE_GAIN(1024, BU27034_GSEL_1024X),
13262306a36Sopenharmony_ci	GAIN_SCALE_GAIN(2048, BU27034_GSEL_2048X),
13362306a36Sopenharmony_ci	GAIN_SCALE_GAIN(4096, BU27034_GSEL_4096X),
13462306a36Sopenharmony_ci};
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci/*
13762306a36Sopenharmony_ci * The IC has 5 modes for sampling time. 5 mS mode is exceptional as it limits
13862306a36Sopenharmony_ci * the data collection to data0-channel only and cuts the supported range to
13962306a36Sopenharmony_ci * 10 bit. It is not supported by the driver.
14062306a36Sopenharmony_ci *
14162306a36Sopenharmony_ci * "normal" modes are 55, 100, 200 and 400 mS modes - which do have direct
14262306a36Sopenharmony_ci * multiplying impact to the register values (similar to gain).
14362306a36Sopenharmony_ci *
14462306a36Sopenharmony_ci * This means that if meas-mode is changed for example from 400 => 200,
14562306a36Sopenharmony_ci * the scale is doubled. Eg, time impact to total gain is x1, x2, x4, x8.
14662306a36Sopenharmony_ci */
14762306a36Sopenharmony_ci#define BU27034_MEAS_MODE_100MS		0
14862306a36Sopenharmony_ci#define BU27034_MEAS_MODE_55MS		1
14962306a36Sopenharmony_ci#define BU27034_MEAS_MODE_200MS		2
15062306a36Sopenharmony_ci#define BU27034_MEAS_MODE_400MS		4
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic const struct iio_itime_sel_mul bu27034_itimes[] = {
15362306a36Sopenharmony_ci	GAIN_SCALE_ITIME_US(400000, BU27034_MEAS_MODE_400MS, 8),
15462306a36Sopenharmony_ci	GAIN_SCALE_ITIME_US(200000, BU27034_MEAS_MODE_200MS, 4),
15562306a36Sopenharmony_ci	GAIN_SCALE_ITIME_US(100000, BU27034_MEAS_MODE_100MS, 2),
15662306a36Sopenharmony_ci	GAIN_SCALE_ITIME_US(55000, BU27034_MEAS_MODE_55MS, 1),
15762306a36Sopenharmony_ci};
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci#define BU27034_CHAN_DATA(_name, _ch2)					\
16062306a36Sopenharmony_ci{									\
16162306a36Sopenharmony_ci	.type = IIO_INTENSITY,						\
16262306a36Sopenharmony_ci	.channel = BU27034_CHAN_##_name,				\
16362306a36Sopenharmony_ci	.channel2 = (_ch2),						\
16462306a36Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
16562306a36Sopenharmony_ci			      BIT(IIO_CHAN_INFO_SCALE),			\
16662306a36Sopenharmony_ci	.info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE),	\
16762306a36Sopenharmony_ci	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),		\
16862306a36Sopenharmony_ci	.info_mask_shared_by_all_available =				\
16962306a36Sopenharmony_ci					BIT(IIO_CHAN_INFO_INT_TIME),	\
17062306a36Sopenharmony_ci	.address = BU27034_REG_##_name##_LO,				\
17162306a36Sopenharmony_ci	.scan_index = BU27034_CHAN_##_name,				\
17262306a36Sopenharmony_ci	.scan_type = {							\
17362306a36Sopenharmony_ci		.sign = 'u',						\
17462306a36Sopenharmony_ci		.realbits = 16,						\
17562306a36Sopenharmony_ci		.storagebits = 16,					\
17662306a36Sopenharmony_ci		.endianness = IIO_LE,					\
17762306a36Sopenharmony_ci	},								\
17862306a36Sopenharmony_ci	.indexed = 1,							\
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic const struct iio_chan_spec bu27034_channels[] = {
18262306a36Sopenharmony_ci	{
18362306a36Sopenharmony_ci		.type = IIO_LIGHT,
18462306a36Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
18562306a36Sopenharmony_ci				      BIT(IIO_CHAN_INFO_SCALE),
18662306a36Sopenharmony_ci		.channel = BU27034_CHAN_ALS,
18762306a36Sopenharmony_ci		.scan_index = BU27034_CHAN_ALS,
18862306a36Sopenharmony_ci		.scan_type = {
18962306a36Sopenharmony_ci			.sign = 'u',
19062306a36Sopenharmony_ci			.realbits = 32,
19162306a36Sopenharmony_ci			.storagebits = 32,
19262306a36Sopenharmony_ci			.endianness = IIO_CPU,
19362306a36Sopenharmony_ci		},
19462306a36Sopenharmony_ci	},
19562306a36Sopenharmony_ci	/*
19662306a36Sopenharmony_ci	 * The BU27034 DATA0 and DATA1 channels are both on the visible light
19762306a36Sopenharmony_ci	 * area (mostly). The data0 sensitivity peaks at 500nm, DATA1 at 600nm.
19862306a36Sopenharmony_ci	 * These wave lengths are pretty much on the border of colours making
19962306a36Sopenharmony_ci	 * these a poor candidates for R/G/B standardization. Hence they're both
20062306a36Sopenharmony_ci	 * marked as clear channels
20162306a36Sopenharmony_ci	 */
20262306a36Sopenharmony_ci	BU27034_CHAN_DATA(DATA0, IIO_MOD_LIGHT_CLEAR),
20362306a36Sopenharmony_ci	BU27034_CHAN_DATA(DATA1, IIO_MOD_LIGHT_CLEAR),
20462306a36Sopenharmony_ci	BU27034_CHAN_DATA(DATA2, IIO_MOD_LIGHT_IR),
20562306a36Sopenharmony_ci	IIO_CHAN_SOFT_TIMESTAMP(4),
20662306a36Sopenharmony_ci};
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistruct bu27034_data {
20962306a36Sopenharmony_ci	struct regmap *regmap;
21062306a36Sopenharmony_ci	struct device *dev;
21162306a36Sopenharmony_ci	/*
21262306a36Sopenharmony_ci	 * Protect gain and time during scale adjustment and data reading.
21362306a36Sopenharmony_ci	 * Protect measurement enabling/disabling.
21462306a36Sopenharmony_ci	 */
21562306a36Sopenharmony_ci	struct mutex mutex;
21662306a36Sopenharmony_ci	struct iio_gts gts;
21762306a36Sopenharmony_ci	struct task_struct *task;
21862306a36Sopenharmony_ci	__le16 raw[3];
21962306a36Sopenharmony_ci	struct {
22062306a36Sopenharmony_ci		u32 mlux;
22162306a36Sopenharmony_ci		__le16 channels[3];
22262306a36Sopenharmony_ci		s64 ts __aligned(8);
22362306a36Sopenharmony_ci	} scan;
22462306a36Sopenharmony_ci};
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistruct bu27034_result {
22762306a36Sopenharmony_ci	u16 ch0;
22862306a36Sopenharmony_ci	u16 ch1;
22962306a36Sopenharmony_ci	u16 ch2;
23062306a36Sopenharmony_ci};
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic const struct regmap_range bu27034_volatile_ranges[] = {
23362306a36Sopenharmony_ci	{
23462306a36Sopenharmony_ci		.range_min = BU27034_REG_SYSTEM_CONTROL,
23562306a36Sopenharmony_ci		.range_max = BU27034_REG_SYSTEM_CONTROL,
23662306a36Sopenharmony_ci	}, {
23762306a36Sopenharmony_ci		.range_min = BU27034_REG_MODE_CONTROL4,
23862306a36Sopenharmony_ci		.range_max = BU27034_REG_MODE_CONTROL4,
23962306a36Sopenharmony_ci	}, {
24062306a36Sopenharmony_ci		.range_min = BU27034_REG_DATA0_LO,
24162306a36Sopenharmony_ci		.range_max = BU27034_REG_DATA2_HI,
24262306a36Sopenharmony_ci	},
24362306a36Sopenharmony_ci};
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic const struct regmap_access_table bu27034_volatile_regs = {
24662306a36Sopenharmony_ci	.yes_ranges = &bu27034_volatile_ranges[0],
24762306a36Sopenharmony_ci	.n_yes_ranges = ARRAY_SIZE(bu27034_volatile_ranges),
24862306a36Sopenharmony_ci};
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic const struct regmap_range bu27034_read_only_ranges[] = {
25162306a36Sopenharmony_ci	{
25262306a36Sopenharmony_ci		.range_min = BU27034_REG_DATA0_LO,
25362306a36Sopenharmony_ci		.range_max = BU27034_REG_DATA2_HI,
25462306a36Sopenharmony_ci	}, {
25562306a36Sopenharmony_ci		.range_min = BU27034_REG_MANUFACTURER_ID,
25662306a36Sopenharmony_ci		.range_max = BU27034_REG_MANUFACTURER_ID,
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci};
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic const struct regmap_access_table bu27034_ro_regs = {
26162306a36Sopenharmony_ci	.no_ranges = &bu27034_read_only_ranges[0],
26262306a36Sopenharmony_ci	.n_no_ranges = ARRAY_SIZE(bu27034_read_only_ranges),
26362306a36Sopenharmony_ci};
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic const struct regmap_config bu27034_regmap = {
26662306a36Sopenharmony_ci	.reg_bits = 8,
26762306a36Sopenharmony_ci	.val_bits = 8,
26862306a36Sopenharmony_ci	.max_register = BU27034_REG_MAX,
26962306a36Sopenharmony_ci	.cache_type = REGCACHE_RBTREE,
27062306a36Sopenharmony_ci	.volatile_table = &bu27034_volatile_regs,
27162306a36Sopenharmony_ci	.wr_table = &bu27034_ro_regs,
27262306a36Sopenharmony_ci};
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistruct bu27034_gain_check {
27562306a36Sopenharmony_ci	int old_gain;
27662306a36Sopenharmony_ci	int new_gain;
27762306a36Sopenharmony_ci	int chan;
27862306a36Sopenharmony_ci};
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic int bu27034_get_gain_sel(struct bu27034_data *data, int chan)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	int ret, val;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	switch (chan) {
28562306a36Sopenharmony_ci	case BU27034_CHAN_DATA0:
28662306a36Sopenharmony_ci	case BU27034_CHAN_DATA1:
28762306a36Sopenharmony_ci	{
28862306a36Sopenharmony_ci		int reg[] = {
28962306a36Sopenharmony_ci			[BU27034_CHAN_DATA0] = BU27034_REG_MODE_CONTROL2,
29062306a36Sopenharmony_ci			[BU27034_CHAN_DATA1] = BU27034_REG_MODE_CONTROL3,
29162306a36Sopenharmony_ci		};
29262306a36Sopenharmony_ci		ret = regmap_read(data->regmap, reg[chan], &val);
29362306a36Sopenharmony_ci		if (ret)
29462306a36Sopenharmony_ci			return ret;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci		return FIELD_GET(BU27034_MASK_D01_GAIN, val);
29762306a36Sopenharmony_ci	}
29862306a36Sopenharmony_ci	case BU27034_CHAN_DATA2:
29962306a36Sopenharmony_ci	{
30062306a36Sopenharmony_ci		int d2_lo_bits = fls(BU27034_MASK_D2_GAIN_LO);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci		ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL2, &val);
30362306a36Sopenharmony_ci		if (ret)
30462306a36Sopenharmony_ci			return ret;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci		/*
30762306a36Sopenharmony_ci		 * The data2 channel gain is composed by 5 non continuous bits
30862306a36Sopenharmony_ci		 * [7:6], [2:0]. Thus when we combine the 5-bit 'selector'
30962306a36Sopenharmony_ci		 * from register value we must right shift the high bits by 3.
31062306a36Sopenharmony_ci		 */
31162306a36Sopenharmony_ci		return FIELD_GET(BU27034_MASK_D2_GAIN_HI, val) << d2_lo_bits |
31262306a36Sopenharmony_ci		       FIELD_GET(BU27034_MASK_D2_GAIN_LO, val);
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci	default:
31562306a36Sopenharmony_ci		return -EINVAL;
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic int bu27034_get_gain(struct bu27034_data *data, int chan, int *gain)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	int ret, sel;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	ret = bu27034_get_gain_sel(data, chan);
32462306a36Sopenharmony_ci	if (ret < 0)
32562306a36Sopenharmony_ci		return ret;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	sel = ret;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	ret = iio_gts_find_gain_by_sel(&data->gts, sel);
33062306a36Sopenharmony_ci	if (ret < 0) {
33162306a36Sopenharmony_ci		dev_err(data->dev, "chan %u: unknown gain value 0x%x\n", chan,
33262306a36Sopenharmony_ci			sel);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci		return ret;
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	*gain = ret;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	return 0;
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic int bu27034_get_int_time(struct bu27034_data *data)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	int ret, sel;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL1, &sel);
34762306a36Sopenharmony_ci	if (ret)
34862306a36Sopenharmony_ci		return ret;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	return iio_gts_find_int_time_by_sel(&data->gts,
35162306a36Sopenharmony_ci					    sel & BU27034_MASK_MEAS_MODE);
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic int _bu27034_get_scale(struct bu27034_data *data, int channel, int *val,
35562306a36Sopenharmony_ci			      int *val2)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	int gain, ret;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	ret = bu27034_get_gain(data, channel, &gain);
36062306a36Sopenharmony_ci	if (ret)
36162306a36Sopenharmony_ci		return ret;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	ret = bu27034_get_int_time(data);
36462306a36Sopenharmony_ci	if (ret < 0)
36562306a36Sopenharmony_ci		return ret;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	return iio_gts_get_scale(&data->gts, gain, ret, val, val2);
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic int bu27034_get_scale(struct bu27034_data *data, int channel, int *val,
37162306a36Sopenharmony_ci			      int *val2)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	int ret;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	if (channel == BU27034_CHAN_ALS) {
37662306a36Sopenharmony_ci		*val = 0;
37762306a36Sopenharmony_ci		*val2 = 1000;
37862306a36Sopenharmony_ci		return IIO_VAL_INT_PLUS_MICRO;
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	mutex_lock(&data->mutex);
38262306a36Sopenharmony_ci	ret = _bu27034_get_scale(data, channel, val, val2);
38362306a36Sopenharmony_ci	mutex_unlock(&data->mutex);
38462306a36Sopenharmony_ci	if (ret)
38562306a36Sopenharmony_ci		return ret;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	return IIO_VAL_INT_PLUS_NANO;
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci/* Caller should hold the lock to protect lux reading */
39162306a36Sopenharmony_cistatic int bu27034_write_gain_sel(struct bu27034_data *data, int chan, int sel)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	static const int reg[] = {
39462306a36Sopenharmony_ci		[BU27034_CHAN_DATA0] = BU27034_REG_MODE_CONTROL2,
39562306a36Sopenharmony_ci		[BU27034_CHAN_DATA1] = BU27034_REG_MODE_CONTROL3,
39662306a36Sopenharmony_ci	};
39762306a36Sopenharmony_ci	int mask, val;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	if (chan != BU27034_CHAN_DATA0 && chan != BU27034_CHAN_DATA1)
40062306a36Sopenharmony_ci		return -EINVAL;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	val = FIELD_PREP(BU27034_MASK_D01_GAIN, sel);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	mask = BU27034_MASK_D01_GAIN;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	if (chan == BU27034_CHAN_DATA0) {
40762306a36Sopenharmony_ci		/*
40862306a36Sopenharmony_ci		 * We keep the same gain for channel 2 as we set for channel 0
40962306a36Sopenharmony_ci		 * We can't allow them to be individually controlled because
41062306a36Sopenharmony_ci		 * setting one will impact also the other. Also, if we don't
41162306a36Sopenharmony_ci		 * always update both gains we may result unsupported bit
41262306a36Sopenharmony_ci		 * combinations.
41362306a36Sopenharmony_ci		 *
41462306a36Sopenharmony_ci		 * This is not nice but this is yet another place where the
41562306a36Sopenharmony_ci		 * user space must be prepared to surprizes. Namely, see chan 2
41662306a36Sopenharmony_ci		 * gain changed when chan 0 gain is changed.
41762306a36Sopenharmony_ci		 *
41862306a36Sopenharmony_ci		 * This is not fatal for most users though. I don't expect the
41962306a36Sopenharmony_ci		 * channel 2 to be used in any generic cases - the intensity
42062306a36Sopenharmony_ci		 * values provided by the sensor for IR area are not openly
42162306a36Sopenharmony_ci		 * documented. Also, channel 2 is not used for visible light.
42262306a36Sopenharmony_ci		 *
42362306a36Sopenharmony_ci		 * So, if there is application which is written to utilize the
42462306a36Sopenharmony_ci		 * channel 2 - then it is probably specifically targeted to this
42562306a36Sopenharmony_ci		 * sensor and knows how to utilize those values. It is safe to
42662306a36Sopenharmony_ci		 * hope such user can also cope with the gain changes.
42762306a36Sopenharmony_ci		 */
42862306a36Sopenharmony_ci		mask |=  BU27034_MASK_D2_GAIN_LO;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci		/*
43162306a36Sopenharmony_ci		 * The D2 gain bits are directly the lowest bits of selector.
43262306a36Sopenharmony_ci		 * Just do add those bits to the value
43362306a36Sopenharmony_ci		 */
43462306a36Sopenharmony_ci		val |= sel & BU27034_MASK_D2_GAIN_LO;
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	return regmap_update_bits(data->regmap, reg[chan], mask, val);
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cistatic int bu27034_set_gain(struct bu27034_data *data, int chan, int gain)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	int ret;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	/*
44562306a36Sopenharmony_ci	 * We don't allow setting channel 2 gain as it messes up the
44662306a36Sopenharmony_ci	 * gain for channel 0 - which shares the high bits
44762306a36Sopenharmony_ci	 */
44862306a36Sopenharmony_ci	if (chan != BU27034_CHAN_DATA0 && chan != BU27034_CHAN_DATA1)
44962306a36Sopenharmony_ci		return -EINVAL;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	ret = iio_gts_find_sel_by_gain(&data->gts, gain);
45262306a36Sopenharmony_ci	if (ret < 0)
45362306a36Sopenharmony_ci		return ret;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	return bu27034_write_gain_sel(data, chan, ret);
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci/* Caller should hold the lock to protect data->int_time */
45962306a36Sopenharmony_cistatic int bu27034_set_int_time(struct bu27034_data *data, int time)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	int ret;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	ret = iio_gts_find_sel_by_int_time(&data->gts, time);
46462306a36Sopenharmony_ci	if (ret < 0)
46562306a36Sopenharmony_ci		return ret;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	return regmap_update_bits(data->regmap, BU27034_REG_MODE_CONTROL1,
46862306a36Sopenharmony_ci				 BU27034_MASK_MEAS_MODE, ret);
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci/*
47262306a36Sopenharmony_ci * We try to change the time in such way that the scale is maintained for
47362306a36Sopenharmony_ci * given channels by adjusting gain so that it compensates the time change.
47462306a36Sopenharmony_ci */
47562306a36Sopenharmony_cistatic int bu27034_try_set_int_time(struct bu27034_data *data, int time_us)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct bu27034_gain_check gains[] = {
47862306a36Sopenharmony_ci		{ .chan = BU27034_CHAN_DATA0 },
47962306a36Sopenharmony_ci		{ .chan = BU27034_CHAN_DATA1 },
48062306a36Sopenharmony_ci	};
48162306a36Sopenharmony_ci	int numg = ARRAY_SIZE(gains);
48262306a36Sopenharmony_ci	int ret, int_time_old, i;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	mutex_lock(&data->mutex);
48562306a36Sopenharmony_ci	ret = bu27034_get_int_time(data);
48662306a36Sopenharmony_ci	if (ret < 0)
48762306a36Sopenharmony_ci		goto unlock_out;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	int_time_old = ret;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	if (!iio_gts_valid_time(&data->gts, time_us)) {
49262306a36Sopenharmony_ci		dev_err(data->dev, "Unsupported integration time %u\n",
49362306a36Sopenharmony_ci			time_us);
49462306a36Sopenharmony_ci		ret = -EINVAL;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci		goto unlock_out;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (time_us == int_time_old) {
50062306a36Sopenharmony_ci		ret = 0;
50162306a36Sopenharmony_ci		goto unlock_out;
50262306a36Sopenharmony_ci	}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	for (i = 0; i < numg; i++) {
50562306a36Sopenharmony_ci		ret = bu27034_get_gain(data, gains[i].chan, &gains[i].old_gain);
50662306a36Sopenharmony_ci		if (ret)
50762306a36Sopenharmony_ci			goto unlock_out;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci		ret = iio_gts_find_new_gain_by_old_gain_time(&data->gts,
51062306a36Sopenharmony_ci							     gains[i].old_gain,
51162306a36Sopenharmony_ci							     int_time_old, time_us,
51262306a36Sopenharmony_ci							     &gains[i].new_gain);
51362306a36Sopenharmony_ci		if (ret) {
51462306a36Sopenharmony_ci			int scale1, scale2;
51562306a36Sopenharmony_ci			bool ok;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci			_bu27034_get_scale(data, gains[i].chan, &scale1, &scale2);
51862306a36Sopenharmony_ci			dev_dbg(data->dev,
51962306a36Sopenharmony_ci				"chan %u, can't support time %u with scale %u %u\n",
52062306a36Sopenharmony_ci				gains[i].chan, time_us, scale1, scale2);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci			if (gains[i].new_gain < 0)
52362306a36Sopenharmony_ci				goto unlock_out;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci			/*
52662306a36Sopenharmony_ci			 * If caller requests for integration time change and we
52762306a36Sopenharmony_ci			 * can't support the scale - then the caller should be
52862306a36Sopenharmony_ci			 * prepared to 'pick up the pieces and deal with the
52962306a36Sopenharmony_ci			 * fact that the scale changed'.
53062306a36Sopenharmony_ci			 */
53162306a36Sopenharmony_ci			ret = iio_find_closest_gain_low(&data->gts,
53262306a36Sopenharmony_ci							gains[i].new_gain, &ok);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci			if (!ok)
53562306a36Sopenharmony_ci				dev_dbg(data->dev,
53662306a36Sopenharmony_ci					"optimal gain out of range for chan %u\n",
53762306a36Sopenharmony_ci					gains[i].chan);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci			if (ret < 0) {
54062306a36Sopenharmony_ci				dev_dbg(data->dev,
54162306a36Sopenharmony_ci					 "Total gain increase. Risk of saturation");
54262306a36Sopenharmony_ci				ret = iio_gts_get_min_gain(&data->gts);
54362306a36Sopenharmony_ci				if (ret < 0)
54462306a36Sopenharmony_ci					goto unlock_out;
54562306a36Sopenharmony_ci			}
54662306a36Sopenharmony_ci			dev_dbg(data->dev, "chan %u scale changed\n",
54762306a36Sopenharmony_ci				 gains[i].chan);
54862306a36Sopenharmony_ci			gains[i].new_gain = ret;
54962306a36Sopenharmony_ci			dev_dbg(data->dev, "chan %u new gain %u\n",
55062306a36Sopenharmony_ci				gains[i].chan, gains[i].new_gain);
55162306a36Sopenharmony_ci		}
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	for (i = 0; i < numg; i++) {
55562306a36Sopenharmony_ci		ret = bu27034_set_gain(data, gains[i].chan, gains[i].new_gain);
55662306a36Sopenharmony_ci		if (ret)
55762306a36Sopenharmony_ci			goto unlock_out;
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	ret = bu27034_set_int_time(data, time_us);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ciunlock_out:
56362306a36Sopenharmony_ci	mutex_unlock(&data->mutex);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	return ret;
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cistatic int bu27034_set_scale(struct bu27034_data *data, int chan,
56962306a36Sopenharmony_ci			    int val, int val2)
57062306a36Sopenharmony_ci{
57162306a36Sopenharmony_ci	int ret, time_sel, gain_sel, i;
57262306a36Sopenharmony_ci	bool found = false;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	if (chan == BU27034_CHAN_DATA2)
57562306a36Sopenharmony_ci		return -EINVAL;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	if (chan == BU27034_CHAN_ALS) {
57862306a36Sopenharmony_ci		if (val == 0 && val2 == 1000000)
57962306a36Sopenharmony_ci			return 0;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci		return -EINVAL;
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	mutex_lock(&data->mutex);
58562306a36Sopenharmony_ci	ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL1, &time_sel);
58662306a36Sopenharmony_ci	if (ret)
58762306a36Sopenharmony_ci		goto unlock_out;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts, time_sel,
59062306a36Sopenharmony_ci						val, val2, &gain_sel);
59162306a36Sopenharmony_ci	if (ret) {
59262306a36Sopenharmony_ci		/*
59362306a36Sopenharmony_ci		 * Could not support scale with given time. Need to change time.
59462306a36Sopenharmony_ci		 * We still want to maintain the scale for all channels
59562306a36Sopenharmony_ci		 */
59662306a36Sopenharmony_ci		struct bu27034_gain_check gain;
59762306a36Sopenharmony_ci		int new_time_sel;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci		/*
60062306a36Sopenharmony_ci		 * Populate information for the other channel which should also
60162306a36Sopenharmony_ci		 * maintain the scale. (Due to the HW limitations the chan2
60262306a36Sopenharmony_ci		 * gets the same gain as chan0, so we only need to explicitly
60362306a36Sopenharmony_ci		 * set the chan 0 and 1).
60462306a36Sopenharmony_ci		 */
60562306a36Sopenharmony_ci		if (chan == BU27034_CHAN_DATA0)
60662306a36Sopenharmony_ci			gain.chan = BU27034_CHAN_DATA1;
60762306a36Sopenharmony_ci		else if (chan == BU27034_CHAN_DATA1)
60862306a36Sopenharmony_ci			gain.chan = BU27034_CHAN_DATA0;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci		ret = bu27034_get_gain(data, gain.chan, &gain.old_gain);
61162306a36Sopenharmony_ci		if (ret)
61262306a36Sopenharmony_ci			goto unlock_out;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci		/*
61562306a36Sopenharmony_ci		 * Iterate through all the times to see if we find one which
61662306a36Sopenharmony_ci		 * can support requested scale for requested channel, while
61762306a36Sopenharmony_ci		 * maintaining the scale for other channels
61862306a36Sopenharmony_ci		 */
61962306a36Sopenharmony_ci		for (i = 0; i < data->gts.num_itime; i++) {
62062306a36Sopenharmony_ci			new_time_sel = data->gts.itime_table[i].sel;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci			if (new_time_sel == time_sel)
62362306a36Sopenharmony_ci				continue;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci			/* Can we provide requested scale with this time? */
62662306a36Sopenharmony_ci			ret = iio_gts_find_gain_sel_for_scale_using_time(
62762306a36Sopenharmony_ci				&data->gts, new_time_sel, val, val2,
62862306a36Sopenharmony_ci				&gain_sel);
62962306a36Sopenharmony_ci			if (ret)
63062306a36Sopenharmony_ci				continue;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci			/* Can the other channel(s) maintain scale? */
63362306a36Sopenharmony_ci			ret = iio_gts_find_new_gain_sel_by_old_gain_time(
63462306a36Sopenharmony_ci				&data->gts, gain.old_gain, time_sel,
63562306a36Sopenharmony_ci				new_time_sel, &gain.new_gain);
63662306a36Sopenharmony_ci			if (!ret) {
63762306a36Sopenharmony_ci				/* Yes - we found suitable time */
63862306a36Sopenharmony_ci				found = true;
63962306a36Sopenharmony_ci				break;
64062306a36Sopenharmony_ci			}
64162306a36Sopenharmony_ci		}
64262306a36Sopenharmony_ci		if (!found) {
64362306a36Sopenharmony_ci			dev_dbg(data->dev,
64462306a36Sopenharmony_ci				"Can't set scale maintaining other channels\n");
64562306a36Sopenharmony_ci			ret = -EINVAL;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci			goto unlock_out;
64862306a36Sopenharmony_ci		}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci		ret = bu27034_set_gain(data, gain.chan, gain.new_gain);
65162306a36Sopenharmony_ci		if (ret)
65262306a36Sopenharmony_ci			goto unlock_out;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci		ret = regmap_update_bits(data->regmap, BU27034_REG_MODE_CONTROL1,
65562306a36Sopenharmony_ci				  BU27034_MASK_MEAS_MODE, new_time_sel);
65662306a36Sopenharmony_ci		if (ret)
65762306a36Sopenharmony_ci			goto unlock_out;
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	ret = bu27034_write_gain_sel(data, chan, gain_sel);
66162306a36Sopenharmony_ciunlock_out:
66262306a36Sopenharmony_ci	mutex_unlock(&data->mutex);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	return ret;
66562306a36Sopenharmony_ci}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci/*
66862306a36Sopenharmony_ci * for (D1/D0 < 0.87):
66962306a36Sopenharmony_ci * lx = 0.004521097 * D1 - 0.002663996 * D0 +
67062306a36Sopenharmony_ci *	0.00012213 * D1 * D1 / D0
67162306a36Sopenharmony_ci *
67262306a36Sopenharmony_ci * =>	115.7400832 * ch1 / gain1 / mt -
67362306a36Sopenharmony_ci *	68.1982976 * ch0 / gain0 / mt +
67462306a36Sopenharmony_ci *	0.00012213 * 25600 * (ch1 / gain1 / mt) * 25600 *
67562306a36Sopenharmony_ci *	(ch1 /gain1 / mt) / (25600 * ch0 / gain0 / mt)
67662306a36Sopenharmony_ci *
67762306a36Sopenharmony_ci * A =	0.00012213 * 25600 * (ch1 /gain1 / mt) * 25600 *
67862306a36Sopenharmony_ci *	(ch1 /gain1 / mt) / (25600 * ch0 / gain0 / mt)
67962306a36Sopenharmony_ci * =>	0.00012213 * 25600 * (ch1 /gain1 / mt) *
68062306a36Sopenharmony_ci *	(ch1 /gain1 / mt) / (ch0 / gain0 / mt)
68162306a36Sopenharmony_ci * =>	0.00012213 * 25600 * (ch1 / gain1) * (ch1 /gain1 / mt) /
68262306a36Sopenharmony_ci *	(ch0 / gain0)
68362306a36Sopenharmony_ci * =>	0.00012213 * 25600 * (ch1 / gain1) * (ch1 /gain1 / mt) *
68462306a36Sopenharmony_ci *	gain0 / ch0
68562306a36Sopenharmony_ci * =>	3.126528 * ch1 * ch1 * gain0 / gain1 / gain1 / mt /ch0
68662306a36Sopenharmony_ci *
68762306a36Sopenharmony_ci * lx = (115.7400832 * ch1 / gain1 - 68.1982976 * ch0 / gain0) /
68862306a36Sopenharmony_ci *	mt + A
68962306a36Sopenharmony_ci * =>	(115.7400832 * ch1 / gain1 - 68.1982976 * ch0 / gain0) /
69062306a36Sopenharmony_ci *	mt + 3.126528 * ch1 * ch1 * gain0 / gain1 / gain1 / mt /
69162306a36Sopenharmony_ci *	ch0
69262306a36Sopenharmony_ci *
69362306a36Sopenharmony_ci * =>	(115.7400832 * ch1 / gain1 - 68.1982976 * ch0 / gain0 +
69462306a36Sopenharmony_ci *	  3.126528 * ch1 * ch1 * gain0 / gain1 / gain1 / ch0) /
69562306a36Sopenharmony_ci *	  mt
69662306a36Sopenharmony_ci *
69762306a36Sopenharmony_ci * For (0.87 <= D1/D0 < 1.00)
69862306a36Sopenharmony_ci * lx = (0.001331* D0 + 0.0000354 * D1) * ((D1/D0 – 0.87) * (0.385) + 1)
69962306a36Sopenharmony_ci * =>	(0.001331 * 256 * 100 * ch0 / gain0 / mt + 0.0000354 * 256 *
70062306a36Sopenharmony_ci *	100 * ch1 / gain1 / mt) * ((D1/D0 -  0.87) * (0.385) + 1)
70162306a36Sopenharmony_ci * =>	(34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) *
70262306a36Sopenharmony_ci *	((D1/D0 -  0.87) * (0.385) + 1)
70362306a36Sopenharmony_ci * =>	(34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) *
70462306a36Sopenharmony_ci *	(0.385 * D1/D0 - 0.66505)
70562306a36Sopenharmony_ci * =>	(34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) *
70662306a36Sopenharmony_ci *	(0.385 * 256 * 100 * ch1 / gain1 / mt / (256 * 100 * ch0 / gain0 / mt) - 0.66505)
70762306a36Sopenharmony_ci * =>	(34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) *
70862306a36Sopenharmony_ci *	(9856 * ch1 / gain1 / mt / (25600 * ch0 / gain0 / mt) + 0.66505)
70962306a36Sopenharmony_ci * =>	13.118336 * ch1 / (gain1 * mt)
71062306a36Sopenharmony_ci *	+ 22.66064768 * ch0 / (gain0 * mt)
71162306a36Sopenharmony_ci *	+ 8931.90144 * ch1 * ch1 * gain0 /
71262306a36Sopenharmony_ci *	  (25600 * ch0 * gain1 * gain1 * mt)
71362306a36Sopenharmony_ci *	+ 0.602694912 * ch1 / (gain1 * mt)
71462306a36Sopenharmony_ci *
71562306a36Sopenharmony_ci * =>	[0.3489024 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1)
71662306a36Sopenharmony_ci *	 + 22.66064768 * ch0 / gain0
71762306a36Sopenharmony_ci *	 + 13.721030912 * ch1 / gain1
71862306a36Sopenharmony_ci *	] / mt
71962306a36Sopenharmony_ci *
72062306a36Sopenharmony_ci * For (D1/D0 >= 1.00)
72162306a36Sopenharmony_ci *
72262306a36Sopenharmony_ci * lx	= (0.001331* D0 + 0.0000354 * D1) * ((D1/D0 – 2.0) * (-0.05) + 1)
72362306a36Sopenharmony_ci *	=> (0.001331* D0 + 0.0000354 * D1) * (-0.05D1/D0 + 1.1)
72462306a36Sopenharmony_ci *	=> (0.001331 * 256 * 100 * ch0 / gain0 / mt + 0.0000354 * 256 *
72562306a36Sopenharmony_ci *	   100 * ch1 / gain1 / mt) * (-0.05D1/D0 + 1.1)
72662306a36Sopenharmony_ci *	=> (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) *
72762306a36Sopenharmony_ci *	   (-0.05 * 256 * 100 * ch1 / gain1 / mt / (256 * 100 * ch0 / gain0 / mt) + 1.1)
72862306a36Sopenharmony_ci *	=> (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) *
72962306a36Sopenharmony_ci *	   (-1280 * ch1 / (gain1 * mt * 25600 * ch0 / gain0 / mt) + 1.1)
73062306a36Sopenharmony_ci *	=> (34.0736 * ch0 * -1280 * ch1 * gain0 * mt /( gain0 * mt * gain1 * mt * 25600 * ch0)
73162306a36Sopenharmony_ci *	    + 34.0736 * 1.1 * ch0 / (gain0 * mt)
73262306a36Sopenharmony_ci *	    + 0.90624 * ch1 * -1280 * ch1 *gain0 * mt / (gain1 * mt *gain1 * mt * 25600 * ch0)
73362306a36Sopenharmony_ci *	    + 1.1 * 0.90624 * ch1 / (gain1 * mt)
73462306a36Sopenharmony_ci *	=> -43614.208 * ch1 / (gain1 * mt * 25600)
73562306a36Sopenharmony_ci *	    + 37.48096  ch0 / (gain0 * mt)
73662306a36Sopenharmony_ci *	    - 1159.9872 * ch1 * ch1 * gain0 / (gain1 * gain1 * mt * 25600 * ch0)
73762306a36Sopenharmony_ci *	    + 0.996864 ch1 / (gain1 * mt)
73862306a36Sopenharmony_ci *	=> [
73962306a36Sopenharmony_ci *		- 0.045312 * ch1 * ch1 * gain0 / (gain1 * gain1 * ch0)
74062306a36Sopenharmony_ci *		- 0.706816 * ch1 / gain1
74162306a36Sopenharmony_ci *		+ 37.48096  ch0 /gain0
74262306a36Sopenharmony_ci *	   ] * mt
74362306a36Sopenharmony_ci *
74462306a36Sopenharmony_ci *
74562306a36Sopenharmony_ci * So, the first case (D1/D0 < 0.87) can be computed to a form:
74662306a36Sopenharmony_ci *
74762306a36Sopenharmony_ci * lx = (3.126528 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) +
74862306a36Sopenharmony_ci *	 115.7400832 * ch1 / gain1 +
74962306a36Sopenharmony_ci *	-68.1982976 * ch0 / gain0
75062306a36Sopenharmony_ci *	 / mt
75162306a36Sopenharmony_ci *
75262306a36Sopenharmony_ci * Second case (0.87 <= D1/D0 < 1.00) goes to form:
75362306a36Sopenharmony_ci *
75462306a36Sopenharmony_ci *	=> [0.3489024 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) +
75562306a36Sopenharmony_ci *	    13.721030912 * ch1 / gain1 +
75662306a36Sopenharmony_ci *	    22.66064768 * ch0 / gain0
75762306a36Sopenharmony_ci *	   ] / mt
75862306a36Sopenharmony_ci *
75962306a36Sopenharmony_ci * Third case (D1/D0 >= 1.00) goes to form:
76062306a36Sopenharmony_ci *	=> [-0.045312 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) +
76162306a36Sopenharmony_ci *	    -0.706816 * ch1 / gain1 +
76262306a36Sopenharmony_ci *	    37.48096  ch0 /(gain0
76362306a36Sopenharmony_ci *	   ] / mt
76462306a36Sopenharmony_ci *
76562306a36Sopenharmony_ci * This can be unified to format:
76662306a36Sopenharmony_ci * lx = [
76762306a36Sopenharmony_ci *	 A * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) +
76862306a36Sopenharmony_ci *	 B * ch1 / gain1 +
76962306a36Sopenharmony_ci *	 C * ch0 / gain0
77062306a36Sopenharmony_ci *	] / mt
77162306a36Sopenharmony_ci *
77262306a36Sopenharmony_ci * For case 1:
77362306a36Sopenharmony_ci * A = 3.126528,
77462306a36Sopenharmony_ci * B = 115.7400832
77562306a36Sopenharmony_ci * C = -68.1982976
77662306a36Sopenharmony_ci *
77762306a36Sopenharmony_ci * For case 2:
77862306a36Sopenharmony_ci * A = 0.3489024
77962306a36Sopenharmony_ci * B = 13.721030912
78062306a36Sopenharmony_ci * C = 22.66064768
78162306a36Sopenharmony_ci *
78262306a36Sopenharmony_ci * For case 3:
78362306a36Sopenharmony_ci * A = -0.045312
78462306a36Sopenharmony_ci * B = -0.706816
78562306a36Sopenharmony_ci * C = 37.48096
78662306a36Sopenharmony_ci */
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_cistruct bu27034_lx_coeff {
78962306a36Sopenharmony_ci	unsigned int A;
79062306a36Sopenharmony_ci	unsigned int B;
79162306a36Sopenharmony_ci	unsigned int C;
79262306a36Sopenharmony_ci	/* Indicate which of the coefficients above are negative */
79362306a36Sopenharmony_ci	bool is_neg[3];
79462306a36Sopenharmony_ci};
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_cistatic inline u64 gain_mul_div_helper(u64 val, unsigned int gain,
79762306a36Sopenharmony_ci				      unsigned int div)
79862306a36Sopenharmony_ci{
79962306a36Sopenharmony_ci	/*
80062306a36Sopenharmony_ci	 * Max gain for a channel is 4096. The max u64 (0xffffffffffffffffULL)
80162306a36Sopenharmony_ci	 * divided by 4096 is 0xFFFFFFFFFFFFF (GENMASK_ULL(51, 0)) (floored).
80262306a36Sopenharmony_ci	 * Thus, the 0xFFFFFFFFFFFFF is the largest value we can safely multiply
80362306a36Sopenharmony_ci	 * with the gain, no matter what gain is set.
80462306a36Sopenharmony_ci	 *
80562306a36Sopenharmony_ci	 * So, multiplication with max gain may overflow if val is greater than
80662306a36Sopenharmony_ci	 * 0xFFFFFFFFFFFFF (52 bits set)..
80762306a36Sopenharmony_ci	 *
80862306a36Sopenharmony_ci	 * If this is the case we divide first.
80962306a36Sopenharmony_ci	 */
81062306a36Sopenharmony_ci	if (val < GENMASK_ULL(51, 0)) {
81162306a36Sopenharmony_ci		val *= gain;
81262306a36Sopenharmony_ci		do_div(val, div);
81362306a36Sopenharmony_ci	} else {
81462306a36Sopenharmony_ci		do_div(val, div);
81562306a36Sopenharmony_ci		val *= gain;
81662306a36Sopenharmony_ci	}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	return val;
81962306a36Sopenharmony_ci}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_cistatic u64 bu27034_fixp_calc_t1_64bit(unsigned int coeff, unsigned int ch0,
82262306a36Sopenharmony_ci				      unsigned int ch1, unsigned int gain0,
82362306a36Sopenharmony_ci				      unsigned int gain1)
82462306a36Sopenharmony_ci{
82562306a36Sopenharmony_ci	unsigned int helper;
82662306a36Sopenharmony_ci	u64 helper64;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	helper64 = (u64)coeff * (u64)ch1 * (u64)ch1;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	helper = gain1 * gain1;
83162306a36Sopenharmony_ci	if (helper > ch0) {
83262306a36Sopenharmony_ci		do_div(helper64, helper);
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci		return gain_mul_div_helper(helper64, gain0, ch0);
83562306a36Sopenharmony_ci	}
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	do_div(helper64, ch0);
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	return gain_mul_div_helper(helper64, gain0, helper);
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_cistatic u64 bu27034_fixp_calc_t1(unsigned int coeff, unsigned int ch0,
84462306a36Sopenharmony_ci				unsigned int ch1, unsigned int gain0,
84562306a36Sopenharmony_ci				unsigned int gain1)
84662306a36Sopenharmony_ci{
84762306a36Sopenharmony_ci	unsigned int helper, tmp;
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	/*
85062306a36Sopenharmony_ci	 * Here we could overflow even the 64bit value. Hence we
85162306a36Sopenharmony_ci	 * multiply with gain0 only after the divisions - even though
85262306a36Sopenharmony_ci	 * it may result loss of accuracy
85362306a36Sopenharmony_ci	 */
85462306a36Sopenharmony_ci	helper = coeff * ch1 * ch1;
85562306a36Sopenharmony_ci	tmp = helper * gain0;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	helper = ch1 * ch1;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	if (check_mul_overflow(helper, coeff, &helper))
86062306a36Sopenharmony_ci		return bu27034_fixp_calc_t1_64bit(coeff, ch0, ch1, gain0, gain1);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	if (check_mul_overflow(helper, gain0, &tmp))
86362306a36Sopenharmony_ci		return bu27034_fixp_calc_t1_64bit(coeff, ch0, ch1, gain0, gain1);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	return tmp / (gain1 * gain1) / ch0;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci}
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_cistatic u64 bu27034_fixp_calc_t23(unsigned int coeff, unsigned int ch,
87062306a36Sopenharmony_ci				 unsigned int gain)
87162306a36Sopenharmony_ci{
87262306a36Sopenharmony_ci	unsigned int helper;
87362306a36Sopenharmony_ci	u64 helper64;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	if (!check_mul_overflow(coeff, ch, &helper))
87662306a36Sopenharmony_ci		return helper / gain;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	helper64 = (u64)coeff * (u64)ch;
87962306a36Sopenharmony_ci	do_div(helper64, gain);
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	return helper64;
88262306a36Sopenharmony_ci}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_cistatic int bu27034_fixp_calc_lx(unsigned int ch0, unsigned int ch1,
88562306a36Sopenharmony_ci				unsigned int gain0, unsigned int gain1,
88662306a36Sopenharmony_ci				unsigned int meastime, int coeff_idx)
88762306a36Sopenharmony_ci{
88862306a36Sopenharmony_ci	static const struct bu27034_lx_coeff coeff[] = {
88962306a36Sopenharmony_ci		{
89062306a36Sopenharmony_ci			.A = 31265280,		/* 3.126528 */
89162306a36Sopenharmony_ci			.B = 1157400832,	/*115.7400832 */
89262306a36Sopenharmony_ci			.C = 681982976,		/* -68.1982976 */
89362306a36Sopenharmony_ci			.is_neg = {false, false, true},
89462306a36Sopenharmony_ci		}, {
89562306a36Sopenharmony_ci			.A = 3489024,		/* 0.3489024 */
89662306a36Sopenharmony_ci			.B = 137210309,		/* 13.721030912 */
89762306a36Sopenharmony_ci			.C = 226606476,		/* 22.66064768 */
89862306a36Sopenharmony_ci			/* All terms positive */
89962306a36Sopenharmony_ci		}, {
90062306a36Sopenharmony_ci			.A = 453120,		/* -0.045312 */
90162306a36Sopenharmony_ci			.B = 7068160,		/* -0.706816 */
90262306a36Sopenharmony_ci			.C = 374809600,		/* 37.48096 */
90362306a36Sopenharmony_ci			.is_neg = {true, true, false},
90462306a36Sopenharmony_ci		}
90562306a36Sopenharmony_ci	};
90662306a36Sopenharmony_ci	const struct bu27034_lx_coeff *c = &coeff[coeff_idx];
90762306a36Sopenharmony_ci	u64 res = 0, terms[3];
90862306a36Sopenharmony_ci	int i;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	if (coeff_idx >= ARRAY_SIZE(coeff))
91162306a36Sopenharmony_ci		return -EINVAL;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	terms[0] = bu27034_fixp_calc_t1(c->A, ch0, ch1, gain0, gain1);
91462306a36Sopenharmony_ci	terms[1] = bu27034_fixp_calc_t23(c->B, ch1, gain1);
91562306a36Sopenharmony_ci	terms[2] = bu27034_fixp_calc_t23(c->C, ch0, gain0);
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	/* First, add positive terms */
91862306a36Sopenharmony_ci	for (i = 0; i < 3; i++)
91962306a36Sopenharmony_ci		if (!c->is_neg[i])
92062306a36Sopenharmony_ci			res += terms[i];
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	/* No positive term => zero lux */
92362306a36Sopenharmony_ci	if (!res)
92462306a36Sopenharmony_ci		return 0;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	/* Then, subtract negative terms (if any) */
92762306a36Sopenharmony_ci	for (i = 0; i < 3; i++)
92862306a36Sopenharmony_ci		if (c->is_neg[i]) {
92962306a36Sopenharmony_ci			/*
93062306a36Sopenharmony_ci			 * If the negative term is greater than positive - then
93162306a36Sopenharmony_ci			 * the darkness has taken over and we are all doomed! Eh,
93262306a36Sopenharmony_ci			 * I mean, then we can just return 0 lx and go out
93362306a36Sopenharmony_ci			 */
93462306a36Sopenharmony_ci			if (terms[i] >= res)
93562306a36Sopenharmony_ci				return 0;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci			res -= terms[i];
93862306a36Sopenharmony_ci		}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	meastime *= 10;
94162306a36Sopenharmony_ci	do_div(res, meastime);
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	return (int) res;
94462306a36Sopenharmony_ci}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_cistatic bool bu27034_has_valid_sample(struct bu27034_data *data)
94762306a36Sopenharmony_ci{
94862306a36Sopenharmony_ci	int ret, val;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL4, &val);
95162306a36Sopenharmony_ci	if (ret) {
95262306a36Sopenharmony_ci		dev_err(data->dev, "Read failed %d\n", ret);
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci		return false;
95562306a36Sopenharmony_ci	}
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	return val & BU27034_MASK_VALID;
95862306a36Sopenharmony_ci}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci/*
96162306a36Sopenharmony_ci * Reading the register where VALID bit is clears this bit. (So does changing
96262306a36Sopenharmony_ci * any gain / integration time configuration registers) The bit gets
96362306a36Sopenharmony_ci * set when we have acquired new data. We use this bit to indicate data
96462306a36Sopenharmony_ci * validity.
96562306a36Sopenharmony_ci */
96662306a36Sopenharmony_cistatic void bu27034_invalidate_read_data(struct bu27034_data *data)
96762306a36Sopenharmony_ci{
96862306a36Sopenharmony_ci	bu27034_has_valid_sample(data);
96962306a36Sopenharmony_ci}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_cistatic int bu27034_read_result(struct bu27034_data *data, int chan, int *res)
97262306a36Sopenharmony_ci{
97362306a36Sopenharmony_ci	int reg[] = {
97462306a36Sopenharmony_ci		[BU27034_CHAN_DATA0] = BU27034_REG_DATA0_LO,
97562306a36Sopenharmony_ci		[BU27034_CHAN_DATA1] = BU27034_REG_DATA1_LO,
97662306a36Sopenharmony_ci		[BU27034_CHAN_DATA2] = BU27034_REG_DATA2_LO,
97762306a36Sopenharmony_ci	};
97862306a36Sopenharmony_ci	int valid, ret;
97962306a36Sopenharmony_ci	__le16 val;
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	ret = regmap_read_poll_timeout(data->regmap, BU27034_REG_MODE_CONTROL4,
98262306a36Sopenharmony_ci				       valid, (valid & BU27034_MASK_VALID),
98362306a36Sopenharmony_ci				       BU27034_DATA_WAIT_TIME_US, 0);
98462306a36Sopenharmony_ci	if (ret)
98562306a36Sopenharmony_ci		return ret;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	ret = regmap_bulk_read(data->regmap, reg[chan], &val, sizeof(val));
98862306a36Sopenharmony_ci	if (ret)
98962306a36Sopenharmony_ci		return ret;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	*res = le16_to_cpu(val);
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	return 0;
99462306a36Sopenharmony_ci}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_cistatic int bu27034_get_result_unlocked(struct bu27034_data *data, __le16 *res,
99762306a36Sopenharmony_ci				       int size)
99862306a36Sopenharmony_ci{
99962306a36Sopenharmony_ci	int ret = 0, retry_cnt = 0;
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ciretry:
100262306a36Sopenharmony_ci	/* Get new value from sensor if data is ready */
100362306a36Sopenharmony_ci	if (bu27034_has_valid_sample(data)) {
100462306a36Sopenharmony_ci		ret = regmap_bulk_read(data->regmap, BU27034_REG_DATA0_LO,
100562306a36Sopenharmony_ci				       res, size);
100662306a36Sopenharmony_ci		if (ret)
100762306a36Sopenharmony_ci			return ret;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci		bu27034_invalidate_read_data(data);
101062306a36Sopenharmony_ci	} else {
101162306a36Sopenharmony_ci		/* No new data in sensor. Wait and retry */
101262306a36Sopenharmony_ci		retry_cnt++;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci		if (retry_cnt > BU27034_RETRY_LIMIT) {
101562306a36Sopenharmony_ci			dev_err(data->dev, "No data from sensor\n");
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci			return -ETIMEDOUT;
101862306a36Sopenharmony_ci		}
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci		msleep(25);
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci		goto retry;
102362306a36Sopenharmony_ci	}
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	return ret;
102662306a36Sopenharmony_ci}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_cistatic int bu27034_meas_set(struct bu27034_data *data, bool en)
102962306a36Sopenharmony_ci{
103062306a36Sopenharmony_ci	if (en)
103162306a36Sopenharmony_ci		return regmap_set_bits(data->regmap, BU27034_REG_MODE_CONTROL4,
103262306a36Sopenharmony_ci				       BU27034_MASK_MEAS_EN);
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	return regmap_clear_bits(data->regmap, BU27034_REG_MODE_CONTROL4,
103562306a36Sopenharmony_ci				 BU27034_MASK_MEAS_EN);
103662306a36Sopenharmony_ci}
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_cistatic int bu27034_get_single_result(struct bu27034_data *data, int chan,
103962306a36Sopenharmony_ci				     int *val)
104062306a36Sopenharmony_ci{
104162306a36Sopenharmony_ci	int ret;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	if (chan < BU27034_CHAN_DATA0 || chan > BU27034_CHAN_DATA2)
104462306a36Sopenharmony_ci		return -EINVAL;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	ret = bu27034_meas_set(data, true);
104762306a36Sopenharmony_ci	if (ret)
104862306a36Sopenharmony_ci		return ret;
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	ret = bu27034_get_int_time(data);
105162306a36Sopenharmony_ci	if (ret < 0)
105262306a36Sopenharmony_ci		return ret;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	msleep(ret / 1000);
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	return bu27034_read_result(data, chan, val);
105762306a36Sopenharmony_ci}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci/*
106062306a36Sopenharmony_ci * The formula given by vendor for computing luxes out of data0 and data1
106162306a36Sopenharmony_ci * (in open air) is as follows:
106262306a36Sopenharmony_ci *
106362306a36Sopenharmony_ci * Let's mark:
106462306a36Sopenharmony_ci * D0 = data0/ch0_gain/meas_time_ms * 25600
106562306a36Sopenharmony_ci * D1 = data1/ch1_gain/meas_time_ms * 25600
106662306a36Sopenharmony_ci *
106762306a36Sopenharmony_ci * Then:
106862306a36Sopenharmony_ci * if (D1/D0 < 0.87)
106962306a36Sopenharmony_ci *	lx = (0.001331 * D0 + 0.0000354 * D1) * ((D1 / D0 - 0.87) * 3.45 + 1)
107062306a36Sopenharmony_ci * else if (D1/D0 < 1)
107162306a36Sopenharmony_ci *	lx = (0.001331 * D0 + 0.0000354 * D1) * ((D1 / D0 - 0.87) * 0.385 + 1)
107262306a36Sopenharmony_ci * else
107362306a36Sopenharmony_ci *	lx = (0.001331 * D0 + 0.0000354 * D1) * ((D1 / D0 - 2) * -0.05 + 1)
107462306a36Sopenharmony_ci *
107562306a36Sopenharmony_ci * We use it here. Users who have for example some colored lens
107662306a36Sopenharmony_ci * need to modify the calculation but I hope this gives a starting point for
107762306a36Sopenharmony_ci * those working with such devices.
107862306a36Sopenharmony_ci */
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_cistatic int bu27034_calc_mlux(struct bu27034_data *data, __le16 *res, int *val)
108162306a36Sopenharmony_ci{
108262306a36Sopenharmony_ci	unsigned int gain0, gain1, meastime;
108362306a36Sopenharmony_ci	unsigned int d1_d0_ratio_scaled;
108462306a36Sopenharmony_ci	u16 ch0, ch1;
108562306a36Sopenharmony_ci	u64 helper64;
108662306a36Sopenharmony_ci	int ret;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	/*
108962306a36Sopenharmony_ci	 * We return 0 lux if calculation fails. This should be reasonably
109062306a36Sopenharmony_ci	 * easy to spot from the buffers especially if raw-data channels show
109162306a36Sopenharmony_ci	 * valid values
109262306a36Sopenharmony_ci	 */
109362306a36Sopenharmony_ci	*val = 0;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	ch0 = max_t(u16, 1, le16_to_cpu(res[0]));
109662306a36Sopenharmony_ci	ch1 = max_t(u16, 1, le16_to_cpu(res[1]));
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	ret = bu27034_get_gain(data, BU27034_CHAN_DATA0, &gain0);
109962306a36Sopenharmony_ci	if (ret)
110062306a36Sopenharmony_ci		return ret;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	ret = bu27034_get_gain(data, BU27034_CHAN_DATA1, &gain1);
110362306a36Sopenharmony_ci	if (ret)
110462306a36Sopenharmony_ci		return ret;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	ret = bu27034_get_int_time(data);
110762306a36Sopenharmony_ci	if (ret < 0)
110862306a36Sopenharmony_ci		return ret;
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	meastime = ret;
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	d1_d0_ratio_scaled = (unsigned int)ch1 * (unsigned int)gain0 * 100;
111362306a36Sopenharmony_ci	helper64 = (u64)ch1 * (u64)gain0 * 100LLU;
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	if (helper64 != d1_d0_ratio_scaled) {
111662306a36Sopenharmony_ci		unsigned int div = (unsigned int)ch0 * gain1;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci		do_div(helper64, div);
111962306a36Sopenharmony_ci		d1_d0_ratio_scaled = helper64;
112062306a36Sopenharmony_ci	} else {
112162306a36Sopenharmony_ci		d1_d0_ratio_scaled /= ch0 * gain1;
112262306a36Sopenharmony_ci	}
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	if (d1_d0_ratio_scaled < 87)
112562306a36Sopenharmony_ci		ret = bu27034_fixp_calc_lx(ch0, ch1, gain0, gain1, meastime, 0);
112662306a36Sopenharmony_ci	else if (d1_d0_ratio_scaled < 100)
112762306a36Sopenharmony_ci		ret = bu27034_fixp_calc_lx(ch0, ch1, gain0, gain1, meastime, 1);
112862306a36Sopenharmony_ci	else
112962306a36Sopenharmony_ci		ret = bu27034_fixp_calc_lx(ch0, ch1, gain0, gain1, meastime, 2);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	if (ret < 0)
113262306a36Sopenharmony_ci		return ret;
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	*val = ret;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	return 0;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci}
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_cistatic int bu27034_get_mlux(struct bu27034_data *data, int chan, int *val)
114162306a36Sopenharmony_ci{
114262306a36Sopenharmony_ci	__le16 res[3];
114362306a36Sopenharmony_ci	int ret;
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	ret = bu27034_meas_set(data, true);
114662306a36Sopenharmony_ci	if (ret)
114762306a36Sopenharmony_ci		return ret;
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	ret = bu27034_get_result_unlocked(data, &res[0], sizeof(res));
115062306a36Sopenharmony_ci	if (ret)
115162306a36Sopenharmony_ci		return ret;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	ret = bu27034_calc_mlux(data, res, val);
115462306a36Sopenharmony_ci	if (ret)
115562306a36Sopenharmony_ci		return ret;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	ret = bu27034_meas_set(data, false);
115862306a36Sopenharmony_ci	if (ret)
115962306a36Sopenharmony_ci		dev_err(data->dev, "failed to disable measurement\n");
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	return 0;
116262306a36Sopenharmony_ci}
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_cistatic int bu27034_read_raw(struct iio_dev *idev,
116562306a36Sopenharmony_ci			   struct iio_chan_spec const *chan,
116662306a36Sopenharmony_ci			   int *val, int *val2, long mask)
116762306a36Sopenharmony_ci{
116862306a36Sopenharmony_ci	struct bu27034_data *data = iio_priv(idev);
116962306a36Sopenharmony_ci	int ret;
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	switch (mask) {
117262306a36Sopenharmony_ci	case IIO_CHAN_INFO_INT_TIME:
117362306a36Sopenharmony_ci		*val = 0;
117462306a36Sopenharmony_ci		*val2 = bu27034_get_int_time(data);
117562306a36Sopenharmony_ci		if (*val2 < 0)
117662306a36Sopenharmony_ci			return *val2;
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci		return IIO_VAL_INT_PLUS_MICRO;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
118162306a36Sopenharmony_ci		return bu27034_get_scale(data, chan->channel, val, val2);
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
118462306a36Sopenharmony_ci	{
118562306a36Sopenharmony_ci		int (*result_get)(struct bu27034_data *data, int chan, int *val);
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci		if (chan->type == IIO_INTENSITY)
118862306a36Sopenharmony_ci			result_get = bu27034_get_single_result;
118962306a36Sopenharmony_ci		else if (chan->type == IIO_LIGHT)
119062306a36Sopenharmony_ci			result_get = bu27034_get_mlux;
119162306a36Sopenharmony_ci		else
119262306a36Sopenharmony_ci			return -EINVAL;
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci		/* Don't mess with measurement enabling while buffering */
119562306a36Sopenharmony_ci		ret = iio_device_claim_direct_mode(idev);
119662306a36Sopenharmony_ci		if (ret)
119762306a36Sopenharmony_ci			return ret;
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci		mutex_lock(&data->mutex);
120062306a36Sopenharmony_ci		/*
120162306a36Sopenharmony_ci		 * Reading one channel at a time is inefficient but we
120262306a36Sopenharmony_ci		 * don't care here. Buffered version should be used if
120362306a36Sopenharmony_ci		 * performance is an issue.
120462306a36Sopenharmony_ci		 */
120562306a36Sopenharmony_ci		ret = result_get(data, chan->channel, val);
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci		mutex_unlock(&data->mutex);
120862306a36Sopenharmony_ci		iio_device_release_direct_mode(idev);
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci		if (ret)
121162306a36Sopenharmony_ci			return ret;
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci		return IIO_VAL_INT;
121462306a36Sopenharmony_ci	}
121562306a36Sopenharmony_ci	default:
121662306a36Sopenharmony_ci		return -EINVAL;
121762306a36Sopenharmony_ci	}
121862306a36Sopenharmony_ci}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_cistatic int bu27034_write_raw_get_fmt(struct iio_dev *indio_dev,
122162306a36Sopenharmony_ci				     struct iio_chan_spec const *chan,
122262306a36Sopenharmony_ci				     long mask)
122362306a36Sopenharmony_ci{
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	switch (mask) {
122662306a36Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
122762306a36Sopenharmony_ci		return IIO_VAL_INT_PLUS_NANO;
122862306a36Sopenharmony_ci	case IIO_CHAN_INFO_INT_TIME:
122962306a36Sopenharmony_ci		return IIO_VAL_INT_PLUS_MICRO;
123062306a36Sopenharmony_ci	default:
123162306a36Sopenharmony_ci		return -EINVAL;
123262306a36Sopenharmony_ci	}
123362306a36Sopenharmony_ci}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_cistatic int bu27034_write_raw(struct iio_dev *idev,
123662306a36Sopenharmony_ci			     struct iio_chan_spec const *chan,
123762306a36Sopenharmony_ci			     int val, int val2, long mask)
123862306a36Sopenharmony_ci{
123962306a36Sopenharmony_ci	struct bu27034_data *data = iio_priv(idev);
124062306a36Sopenharmony_ci	int ret;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	ret = iio_device_claim_direct_mode(idev);
124362306a36Sopenharmony_ci	if (ret)
124462306a36Sopenharmony_ci		return ret;
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	switch (mask) {
124762306a36Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
124862306a36Sopenharmony_ci		ret = bu27034_set_scale(data, chan->channel, val, val2);
124962306a36Sopenharmony_ci		break;
125062306a36Sopenharmony_ci	case IIO_CHAN_INFO_INT_TIME:
125162306a36Sopenharmony_ci		if (!val)
125262306a36Sopenharmony_ci			ret = bu27034_try_set_int_time(data, val2);
125362306a36Sopenharmony_ci		else
125462306a36Sopenharmony_ci			ret = -EINVAL;
125562306a36Sopenharmony_ci		break;
125662306a36Sopenharmony_ci	default:
125762306a36Sopenharmony_ci		ret = -EINVAL;
125862306a36Sopenharmony_ci		break;
125962306a36Sopenharmony_ci	}
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	iio_device_release_direct_mode(idev);
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	return ret;
126462306a36Sopenharmony_ci}
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_cistatic int bu27034_read_avail(struct iio_dev *idev,
126762306a36Sopenharmony_ci			      struct iio_chan_spec const *chan, const int **vals,
126862306a36Sopenharmony_ci			      int *type, int *length, long mask)
126962306a36Sopenharmony_ci{
127062306a36Sopenharmony_ci	struct bu27034_data *data = iio_priv(idev);
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	switch (mask) {
127362306a36Sopenharmony_ci	case IIO_CHAN_INFO_INT_TIME:
127462306a36Sopenharmony_ci		return iio_gts_avail_times(&data->gts, vals, type, length);
127562306a36Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
127662306a36Sopenharmony_ci		return iio_gts_all_avail_scales(&data->gts, vals, type, length);
127762306a36Sopenharmony_ci	default:
127862306a36Sopenharmony_ci		return -EINVAL;
127962306a36Sopenharmony_ci	}
128062306a36Sopenharmony_ci}
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_cistatic const struct iio_info bu27034_info = {
128362306a36Sopenharmony_ci	.read_raw = &bu27034_read_raw,
128462306a36Sopenharmony_ci	.write_raw = &bu27034_write_raw,
128562306a36Sopenharmony_ci	.write_raw_get_fmt = &bu27034_write_raw_get_fmt,
128662306a36Sopenharmony_ci	.read_avail = &bu27034_read_avail,
128762306a36Sopenharmony_ci};
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_cistatic int bu27034_chip_init(struct bu27034_data *data)
129062306a36Sopenharmony_ci{
129162306a36Sopenharmony_ci	int ret, sel;
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	/* Reset */
129462306a36Sopenharmony_ci	ret = regmap_write_bits(data->regmap, BU27034_REG_SYSTEM_CONTROL,
129562306a36Sopenharmony_ci			   BU27034_MASK_SW_RESET, BU27034_MASK_SW_RESET);
129662306a36Sopenharmony_ci	if (ret)
129762306a36Sopenharmony_ci		return dev_err_probe(data->dev, ret, "Sensor reset failed\n");
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	msleep(1);
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	ret = regmap_reinit_cache(data->regmap, &bu27034_regmap);
130262306a36Sopenharmony_ci	if (ret) {
130362306a36Sopenharmony_ci		dev_err(data->dev, "Failed to reinit reg cache\n");
130462306a36Sopenharmony_ci		return ret;
130562306a36Sopenharmony_ci	}
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	/*
130862306a36Sopenharmony_ci	 * Read integration time here to ensure it is in regmap cache. We do
130962306a36Sopenharmony_ci	 * this to speed-up the int-time acquisition in the start of the buffer
131062306a36Sopenharmony_ci	 * handling thread where longer delays could make it more likely we end
131162306a36Sopenharmony_ci	 * up skipping a sample, and where the longer delays make timestamps
131262306a36Sopenharmony_ci	 * less accurate.
131362306a36Sopenharmony_ci	 */
131462306a36Sopenharmony_ci	ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL1, &sel);
131562306a36Sopenharmony_ci	if (ret)
131662306a36Sopenharmony_ci		dev_err(data->dev, "reading integration time failed\n");
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	return 0;
131962306a36Sopenharmony_ci}
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_cistatic int bu27034_wait_for_data(struct bu27034_data *data)
132262306a36Sopenharmony_ci{
132362306a36Sopenharmony_ci	int ret, val;
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	ret = regmap_read_poll_timeout(data->regmap, BU27034_REG_MODE_CONTROL4,
132662306a36Sopenharmony_ci				       val, val & BU27034_MASK_VALID,
132762306a36Sopenharmony_ci				       BU27034_DATA_WAIT_TIME_US,
132862306a36Sopenharmony_ci				       BU27034_TOTAL_DATA_WAIT_TIME_US);
132962306a36Sopenharmony_ci	if (ret) {
133062306a36Sopenharmony_ci		dev_err(data->dev, "data polling %s\n",
133162306a36Sopenharmony_ci			!(val & BU27034_MASK_VALID) ? "timeout" : "fail");
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci		return ret;
133462306a36Sopenharmony_ci	}
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	ret = regmap_bulk_read(data->regmap, BU27034_REG_DATA0_LO,
133762306a36Sopenharmony_ci			       &data->scan.channels[0],
133862306a36Sopenharmony_ci			       sizeof(data->scan.channels));
133962306a36Sopenharmony_ci	if (ret)
134062306a36Sopenharmony_ci		return ret;
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	bu27034_invalidate_read_data(data);
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	return 0;
134562306a36Sopenharmony_ci}
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_cistatic int bu27034_buffer_thread(void *arg)
134862306a36Sopenharmony_ci{
134962306a36Sopenharmony_ci	struct iio_dev *idev = arg;
135062306a36Sopenharmony_ci	struct bu27034_data *data;
135162306a36Sopenharmony_ci	int wait_ms;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	data = iio_priv(idev);
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	wait_ms = bu27034_get_int_time(data);
135662306a36Sopenharmony_ci	wait_ms /= 1000;
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	wait_ms -= BU27034_MEAS_WAIT_PREMATURE_MS;
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	while (!kthread_should_stop()) {
136162306a36Sopenharmony_ci		int ret;
136262306a36Sopenharmony_ci		int64_t tstamp;
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci		msleep(wait_ms);
136562306a36Sopenharmony_ci		ret = bu27034_wait_for_data(data);
136662306a36Sopenharmony_ci		if (ret)
136762306a36Sopenharmony_ci			continue;
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci		tstamp = iio_get_time_ns(idev);
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci		if (test_bit(BU27034_CHAN_ALS, idev->active_scan_mask)) {
137262306a36Sopenharmony_ci			int mlux;
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci			ret = bu27034_calc_mlux(data, &data->scan.channels[0],
137562306a36Sopenharmony_ci					       &mlux);
137662306a36Sopenharmony_ci			if (ret)
137762306a36Sopenharmony_ci				dev_err(data->dev, "failed to calculate lux\n");
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci			/*
138062306a36Sopenharmony_ci			 * The maximum Milli lux value we get with gain 1x time
138162306a36Sopenharmony_ci			 * 55mS data ch0 = 0xffff ch1 = 0xffff fits in 26 bits
138262306a36Sopenharmony_ci			 * so there should be no problem returning int from
138362306a36Sopenharmony_ci			 * computations and casting it to u32
138462306a36Sopenharmony_ci			 */
138562306a36Sopenharmony_ci			data->scan.mlux = (u32)mlux;
138662306a36Sopenharmony_ci		}
138762306a36Sopenharmony_ci		iio_push_to_buffers_with_timestamp(idev, &data->scan, tstamp);
138862306a36Sopenharmony_ci	}
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	return 0;
139162306a36Sopenharmony_ci}
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_cistatic int bu27034_buffer_enable(struct iio_dev *idev)
139462306a36Sopenharmony_ci{
139562306a36Sopenharmony_ci	struct bu27034_data *data = iio_priv(idev);
139662306a36Sopenharmony_ci	struct task_struct *task;
139762306a36Sopenharmony_ci	int ret;
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	mutex_lock(&data->mutex);
140062306a36Sopenharmony_ci	ret = bu27034_meas_set(data, true);
140162306a36Sopenharmony_ci	if (ret)
140262306a36Sopenharmony_ci		goto unlock_out;
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	task = kthread_run(bu27034_buffer_thread, idev,
140562306a36Sopenharmony_ci				 "bu27034-buffering-%u",
140662306a36Sopenharmony_ci				 iio_device_id(idev));
140762306a36Sopenharmony_ci	if (IS_ERR(task)) {
140862306a36Sopenharmony_ci		ret = PTR_ERR(task);
140962306a36Sopenharmony_ci		goto unlock_out;
141062306a36Sopenharmony_ci	}
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	data->task = task;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ciunlock_out:
141562306a36Sopenharmony_ci	mutex_unlock(&data->mutex);
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	return ret;
141862306a36Sopenharmony_ci}
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_cistatic int bu27034_buffer_disable(struct iio_dev *idev)
142162306a36Sopenharmony_ci{
142262306a36Sopenharmony_ci	struct bu27034_data *data = iio_priv(idev);
142362306a36Sopenharmony_ci	int ret;
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	mutex_lock(&data->mutex);
142662306a36Sopenharmony_ci	if (data->task) {
142762306a36Sopenharmony_ci		kthread_stop(data->task);
142862306a36Sopenharmony_ci		data->task = NULL;
142962306a36Sopenharmony_ci	}
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	ret = bu27034_meas_set(data, false);
143262306a36Sopenharmony_ci	mutex_unlock(&data->mutex);
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	return ret;
143562306a36Sopenharmony_ci}
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_cistatic const struct iio_buffer_setup_ops bu27034_buffer_ops = {
143862306a36Sopenharmony_ci	.postenable = &bu27034_buffer_enable,
143962306a36Sopenharmony_ci	.predisable = &bu27034_buffer_disable,
144062306a36Sopenharmony_ci};
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_cistatic int bu27034_probe(struct i2c_client *i2c)
144362306a36Sopenharmony_ci{
144462306a36Sopenharmony_ci	struct device *dev = &i2c->dev;
144562306a36Sopenharmony_ci	struct bu27034_data *data;
144662306a36Sopenharmony_ci	struct regmap *regmap;
144762306a36Sopenharmony_ci	struct iio_dev *idev;
144862306a36Sopenharmony_ci	unsigned int part_id, reg;
144962306a36Sopenharmony_ci	int ret;
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	regmap = devm_regmap_init_i2c(i2c, &bu27034_regmap);
145262306a36Sopenharmony_ci	if (IS_ERR(regmap))
145362306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(regmap),
145462306a36Sopenharmony_ci				     "Failed to initialize Regmap\n");
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	idev = devm_iio_device_alloc(dev, sizeof(*data));
145762306a36Sopenharmony_ci	if (!idev)
145862306a36Sopenharmony_ci		return -ENOMEM;
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	ret = devm_regulator_get_enable(dev, "vdd");
146162306a36Sopenharmony_ci	if (ret)
146262306a36Sopenharmony_ci		return dev_err_probe(dev, ret, "Failed to get regulator\n");
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	data = iio_priv(idev);
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	ret = regmap_read(regmap, BU27034_REG_SYSTEM_CONTROL, &reg);
146762306a36Sopenharmony_ci	if (ret)
146862306a36Sopenharmony_ci		return dev_err_probe(dev, ret, "Failed to access sensor\n");
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	part_id = FIELD_GET(BU27034_MASK_PART_ID, reg);
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	if (part_id != BU27034_ID)
147362306a36Sopenharmony_ci		dev_warn(dev, "unknown device 0x%x\n", part_id);
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	ret = devm_iio_init_iio_gts(dev, BU27034_SCALE_1X, 0, bu27034_gains,
147662306a36Sopenharmony_ci				    ARRAY_SIZE(bu27034_gains), bu27034_itimes,
147762306a36Sopenharmony_ci				    ARRAY_SIZE(bu27034_itimes), &data->gts);
147862306a36Sopenharmony_ci	if (ret)
147962306a36Sopenharmony_ci		return ret;
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	mutex_init(&data->mutex);
148262306a36Sopenharmony_ci	data->regmap = regmap;
148362306a36Sopenharmony_ci	data->dev = dev;
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	idev->channels = bu27034_channels;
148662306a36Sopenharmony_ci	idev->num_channels = ARRAY_SIZE(bu27034_channels);
148762306a36Sopenharmony_ci	idev->name = "bu27034";
148862306a36Sopenharmony_ci	idev->info = &bu27034_info;
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	idev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
149162306a36Sopenharmony_ci	idev->available_scan_masks = bu27034_scan_masks;
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	ret = bu27034_chip_init(data);
149462306a36Sopenharmony_ci	if (ret)
149562306a36Sopenharmony_ci		return ret;
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	ret = devm_iio_kfifo_buffer_setup(dev, idev, &bu27034_buffer_ops);
149862306a36Sopenharmony_ci	if (ret)
149962306a36Sopenharmony_ci		return dev_err_probe(dev, ret, "buffer setup failed\n");
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	ret = devm_iio_device_register(dev, idev);
150262306a36Sopenharmony_ci	if (ret < 0)
150362306a36Sopenharmony_ci		return dev_err_probe(dev, ret,
150462306a36Sopenharmony_ci				     "Unable to register iio device\n");
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	return ret;
150762306a36Sopenharmony_ci}
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_cistatic const struct of_device_id bu27034_of_match[] = {
151062306a36Sopenharmony_ci	{ .compatible = "rohm,bu27034" },
151162306a36Sopenharmony_ci	{ }
151262306a36Sopenharmony_ci};
151362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, bu27034_of_match);
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_cistatic struct i2c_driver bu27034_i2c_driver = {
151662306a36Sopenharmony_ci	.driver = {
151762306a36Sopenharmony_ci		.name = "bu27034-als",
151862306a36Sopenharmony_ci		.of_match_table = bu27034_of_match,
151962306a36Sopenharmony_ci		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
152062306a36Sopenharmony_ci	},
152162306a36Sopenharmony_ci	.probe = bu27034_probe,
152262306a36Sopenharmony_ci};
152362306a36Sopenharmony_cimodule_i2c_driver(bu27034_i2c_driver);
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
152662306a36Sopenharmony_ciMODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
152762306a36Sopenharmony_ciMODULE_DESCRIPTION("ROHM BU27034 ambient light sensor driver");
152862306a36Sopenharmony_ciMODULE_IMPORT_NS(IIO_GTS_HELPER);
1529