162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Device driver for monitoring ambient light intensity in (lux) and proximity
462306a36Sopenharmony_ci * detection (prox) within the TAOS TSL2571, TSL2671, TMD2671, TSL2771, TMD2771,
562306a36Sopenharmony_ci * TSL2572, TSL2672, TMD2672, TSL2772, and TMD2772 devices.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (c) 2012, TAOS Corporation.
862306a36Sopenharmony_ci * Copyright (c) 2017-2018 Brian Masney <masneyb@onstation.org>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/errno.h>
1362306a36Sopenharmony_ci#include <linux/i2c.h>
1462306a36Sopenharmony_ci#include <linux/interrupt.h>
1562306a36Sopenharmony_ci#include <linux/kernel.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/mutex.h>
1862306a36Sopenharmony_ci#include <linux/property.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <linux/iio/events.h>
2262306a36Sopenharmony_ci#include <linux/iio/iio.h>
2362306a36Sopenharmony_ci#include <linux/iio/sysfs.h>
2462306a36Sopenharmony_ci#include <linux/platform_data/tsl2772.h>
2562306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/* Cal defs */
2862306a36Sopenharmony_ci#define PROX_STAT_CAL			0
2962306a36Sopenharmony_ci#define PROX_STAT_SAMP			1
3062306a36Sopenharmony_ci#define MAX_SAMPLES_CAL			200
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/* TSL2772 Device ID */
3362306a36Sopenharmony_ci#define TRITON_ID			0x00
3462306a36Sopenharmony_ci#define SWORDFISH_ID			0x30
3562306a36Sopenharmony_ci#define HALIBUT_ID			0x20
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/* Lux calculation constants */
3862306a36Sopenharmony_ci#define TSL2772_LUX_CALC_OVER_FLOW	65535
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/*
4162306a36Sopenharmony_ci * TAOS Register definitions - Note: depending on device, some of these register
4262306a36Sopenharmony_ci * are not used and the register address is benign.
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/* Register offsets */
4662306a36Sopenharmony_ci#define TSL2772_MAX_CONFIG_REG		16
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* Device Registers and Masks */
4962306a36Sopenharmony_ci#define TSL2772_CNTRL			0x00
5062306a36Sopenharmony_ci#define TSL2772_ALS_TIME		0X01
5162306a36Sopenharmony_ci#define TSL2772_PRX_TIME		0x02
5262306a36Sopenharmony_ci#define TSL2772_WAIT_TIME		0x03
5362306a36Sopenharmony_ci#define TSL2772_ALS_MINTHRESHLO		0X04
5462306a36Sopenharmony_ci#define TSL2772_ALS_MINTHRESHHI		0X05
5562306a36Sopenharmony_ci#define TSL2772_ALS_MAXTHRESHLO		0X06
5662306a36Sopenharmony_ci#define TSL2772_ALS_MAXTHRESHHI		0X07
5762306a36Sopenharmony_ci#define TSL2772_PRX_MINTHRESHLO		0X08
5862306a36Sopenharmony_ci#define TSL2772_PRX_MINTHRESHHI		0X09
5962306a36Sopenharmony_ci#define TSL2772_PRX_MAXTHRESHLO		0X0A
6062306a36Sopenharmony_ci#define TSL2772_PRX_MAXTHRESHHI		0X0B
6162306a36Sopenharmony_ci#define TSL2772_PERSISTENCE		0x0C
6262306a36Sopenharmony_ci#define TSL2772_ALS_PRX_CONFIG		0x0D
6362306a36Sopenharmony_ci#define TSL2772_PRX_COUNT		0x0E
6462306a36Sopenharmony_ci#define TSL2772_GAIN			0x0F
6562306a36Sopenharmony_ci#define TSL2772_NOTUSED			0x10
6662306a36Sopenharmony_ci#define TSL2772_REVID			0x11
6762306a36Sopenharmony_ci#define TSL2772_CHIPID			0x12
6862306a36Sopenharmony_ci#define TSL2772_STATUS			0x13
6962306a36Sopenharmony_ci#define TSL2772_ALS_CHAN0LO		0x14
7062306a36Sopenharmony_ci#define TSL2772_ALS_CHAN0HI		0x15
7162306a36Sopenharmony_ci#define TSL2772_ALS_CHAN1LO		0x16
7262306a36Sopenharmony_ci#define TSL2772_ALS_CHAN1HI		0x17
7362306a36Sopenharmony_ci#define TSL2772_PRX_LO			0x18
7462306a36Sopenharmony_ci#define TSL2772_PRX_HI			0x19
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/* tsl2772 cmd reg masks */
7762306a36Sopenharmony_ci#define TSL2772_CMD_REG			0x80
7862306a36Sopenharmony_ci#define TSL2772_CMD_SPL_FN		0x60
7962306a36Sopenharmony_ci#define TSL2772_CMD_REPEAT_PROTO	0x00
8062306a36Sopenharmony_ci#define TSL2772_CMD_AUTOINC_PROTO	0x20
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci#define TSL2772_CMD_PROX_INT_CLR	0X05
8362306a36Sopenharmony_ci#define TSL2772_CMD_ALS_INT_CLR		0x06
8462306a36Sopenharmony_ci#define TSL2772_CMD_PROXALS_INT_CLR	0X07
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci/* tsl2772 cntrl reg masks */
8762306a36Sopenharmony_ci#define TSL2772_CNTL_ADC_ENBL		0x02
8862306a36Sopenharmony_ci#define TSL2772_CNTL_PWR_ON		0x01
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci/* tsl2772 status reg masks */
9162306a36Sopenharmony_ci#define TSL2772_STA_ADC_VALID		0x01
9262306a36Sopenharmony_ci#define TSL2772_STA_PRX_VALID		0x02
9362306a36Sopenharmony_ci#define TSL2772_STA_ADC_PRX_VALID	(TSL2772_STA_ADC_VALID | \
9462306a36Sopenharmony_ci					 TSL2772_STA_PRX_VALID)
9562306a36Sopenharmony_ci#define TSL2772_STA_ALS_INTR		0x10
9662306a36Sopenharmony_ci#define TSL2772_STA_PRX_INTR		0x20
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/* tsl2772 cntrl reg masks */
9962306a36Sopenharmony_ci#define TSL2772_CNTL_REG_CLEAR		0x00
10062306a36Sopenharmony_ci#define TSL2772_CNTL_PROX_INT_ENBL	0X20
10162306a36Sopenharmony_ci#define TSL2772_CNTL_ALS_INT_ENBL	0X10
10262306a36Sopenharmony_ci#define TSL2772_CNTL_WAIT_TMR_ENBL	0X08
10362306a36Sopenharmony_ci#define TSL2772_CNTL_PROX_DET_ENBL	0X04
10462306a36Sopenharmony_ci#define TSL2772_CNTL_PWRON		0x01
10562306a36Sopenharmony_ci#define TSL2772_CNTL_ALSPON_ENBL	0x03
10662306a36Sopenharmony_ci#define TSL2772_CNTL_INTALSPON_ENBL	0x13
10762306a36Sopenharmony_ci#define TSL2772_CNTL_PROXPON_ENBL	0x0F
10862306a36Sopenharmony_ci#define TSL2772_CNTL_INTPROXPON_ENBL	0x2F
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci#define TSL2772_ALS_GAIN_TRIM_MIN	250
11162306a36Sopenharmony_ci#define TSL2772_ALS_GAIN_TRIM_MAX	4000
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci#define TSL2772_MAX_PROX_LEDS		2
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci#define TSL2772_BOOT_MIN_SLEEP_TIME	10000
11662306a36Sopenharmony_ci#define TSL2772_BOOT_MAX_SLEEP_TIME	28000
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci/* Device family members */
11962306a36Sopenharmony_cienum {
12062306a36Sopenharmony_ci	tsl2571,
12162306a36Sopenharmony_ci	tsl2671,
12262306a36Sopenharmony_ci	tmd2671,
12362306a36Sopenharmony_ci	tsl2771,
12462306a36Sopenharmony_ci	tmd2771,
12562306a36Sopenharmony_ci	tsl2572,
12662306a36Sopenharmony_ci	tsl2672,
12762306a36Sopenharmony_ci	tmd2672,
12862306a36Sopenharmony_ci	tsl2772,
12962306a36Sopenharmony_ci	tmd2772,
13062306a36Sopenharmony_ci	apds9930,
13162306a36Sopenharmony_ci};
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cienum {
13462306a36Sopenharmony_ci	TSL2772_CHIP_UNKNOWN = 0,
13562306a36Sopenharmony_ci	TSL2772_CHIP_WORKING = 1,
13662306a36Sopenharmony_ci	TSL2772_CHIP_SUSPENDED = 2
13762306a36Sopenharmony_ci};
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cienum {
14062306a36Sopenharmony_ci	TSL2772_SUPPLY_VDD = 0,
14162306a36Sopenharmony_ci	TSL2772_SUPPLY_VDDIO = 1,
14262306a36Sopenharmony_ci	TSL2772_NUM_SUPPLIES = 2
14362306a36Sopenharmony_ci};
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/* Per-device data */
14662306a36Sopenharmony_cistruct tsl2772_als_info {
14762306a36Sopenharmony_ci	u16 als_ch0;
14862306a36Sopenharmony_ci	u16 als_ch1;
14962306a36Sopenharmony_ci	u16 lux;
15062306a36Sopenharmony_ci};
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistruct tsl2772_chip_info {
15362306a36Sopenharmony_ci	int chan_table_elements;
15462306a36Sopenharmony_ci	struct iio_chan_spec channel_with_events[4];
15562306a36Sopenharmony_ci	struct iio_chan_spec channel_without_events[4];
15662306a36Sopenharmony_ci	const struct iio_info *info;
15762306a36Sopenharmony_ci};
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic const int tsl2772_led_currents[][2] = {
16062306a36Sopenharmony_ci	{ 100000, TSL2772_100_mA },
16162306a36Sopenharmony_ci	{  50000, TSL2772_50_mA },
16262306a36Sopenharmony_ci	{  25000, TSL2772_25_mA },
16362306a36Sopenharmony_ci	{  13000, TSL2772_13_mA },
16462306a36Sopenharmony_ci	{      0, 0 }
16562306a36Sopenharmony_ci};
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistruct tsl2772_chip {
16862306a36Sopenharmony_ci	kernel_ulong_t id;
16962306a36Sopenharmony_ci	struct mutex prox_mutex;
17062306a36Sopenharmony_ci	struct mutex als_mutex;
17162306a36Sopenharmony_ci	struct i2c_client *client;
17262306a36Sopenharmony_ci	struct regulator_bulk_data supplies[TSL2772_NUM_SUPPLIES];
17362306a36Sopenharmony_ci	u16 prox_data;
17462306a36Sopenharmony_ci	struct tsl2772_als_info als_cur_info;
17562306a36Sopenharmony_ci	struct tsl2772_settings settings;
17662306a36Sopenharmony_ci	struct tsl2772_platform_data *pdata;
17762306a36Sopenharmony_ci	int als_gain_time_scale;
17862306a36Sopenharmony_ci	int als_saturation;
17962306a36Sopenharmony_ci	int tsl2772_chip_status;
18062306a36Sopenharmony_ci	u8 tsl2772_config[TSL2772_MAX_CONFIG_REG];
18162306a36Sopenharmony_ci	const struct tsl2772_chip_info	*chip_info;
18262306a36Sopenharmony_ci	const struct iio_info *info;
18362306a36Sopenharmony_ci	s64 event_timestamp;
18462306a36Sopenharmony_ci	/*
18562306a36Sopenharmony_ci	 * This structure is intentionally large to accommodate
18662306a36Sopenharmony_ci	 * updates via sysfs.
18762306a36Sopenharmony_ci	 * Sized to 9 = max 8 segments + 1 termination segment
18862306a36Sopenharmony_ci	 */
18962306a36Sopenharmony_ci	struct tsl2772_lux tsl2772_device_lux[TSL2772_MAX_LUX_TABLE_SIZE];
19062306a36Sopenharmony_ci};
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci/*
19362306a36Sopenharmony_ci * Different devices require different coefficents, and these numbers were
19462306a36Sopenharmony_ci * derived from the 'Lux Equation' section of the various device datasheets.
19562306a36Sopenharmony_ci * All of these coefficients assume a Glass Attenuation (GA) factor of 1.
19662306a36Sopenharmony_ci * The coefficients are multiplied by 1000 to avoid floating point operations.
19762306a36Sopenharmony_ci * The two rows in each table correspond to the Lux1 and Lux2 equations from
19862306a36Sopenharmony_ci * the datasheets.
19962306a36Sopenharmony_ci */
20062306a36Sopenharmony_cistatic const struct tsl2772_lux tsl2x71_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = {
20162306a36Sopenharmony_ci	{ 53000, 106000 },
20262306a36Sopenharmony_ci	{ 31800,  53000 },
20362306a36Sopenharmony_ci	{ 0,          0 },
20462306a36Sopenharmony_ci};
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic const struct tsl2772_lux tmd2x71_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = {
20762306a36Sopenharmony_ci	{ 24000,  48000 },
20862306a36Sopenharmony_ci	{ 14400,  24000 },
20962306a36Sopenharmony_ci	{ 0,          0 },
21062306a36Sopenharmony_ci};
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic const struct tsl2772_lux tsl2x72_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = {
21362306a36Sopenharmony_ci	{ 60000, 112200 },
21462306a36Sopenharmony_ci	{ 37800,  60000 },
21562306a36Sopenharmony_ci	{     0,      0 },
21662306a36Sopenharmony_ci};
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic const struct tsl2772_lux tmd2x72_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = {
21962306a36Sopenharmony_ci	{ 20000,  35000 },
22062306a36Sopenharmony_ci	{ 12600,  20000 },
22162306a36Sopenharmony_ci	{     0,      0 },
22262306a36Sopenharmony_ci};
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic const struct tsl2772_lux apds9930_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = {
22562306a36Sopenharmony_ci	{ 52000,  96824 },
22662306a36Sopenharmony_ci	{ 38792,  67132 },
22762306a36Sopenharmony_ci	{     0,      0 },
22862306a36Sopenharmony_ci};
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic const struct tsl2772_lux *tsl2772_default_lux_table_group[] = {
23162306a36Sopenharmony_ci	[tsl2571] = tsl2x71_lux_table,
23262306a36Sopenharmony_ci	[tsl2671] = tsl2x71_lux_table,
23362306a36Sopenharmony_ci	[tmd2671] = tmd2x71_lux_table,
23462306a36Sopenharmony_ci	[tsl2771] = tsl2x71_lux_table,
23562306a36Sopenharmony_ci	[tmd2771] = tmd2x71_lux_table,
23662306a36Sopenharmony_ci	[tsl2572] = tsl2x72_lux_table,
23762306a36Sopenharmony_ci	[tsl2672] = tsl2x72_lux_table,
23862306a36Sopenharmony_ci	[tmd2672] = tmd2x72_lux_table,
23962306a36Sopenharmony_ci	[tsl2772] = tsl2x72_lux_table,
24062306a36Sopenharmony_ci	[tmd2772] = tmd2x72_lux_table,
24162306a36Sopenharmony_ci	[apds9930] = apds9930_lux_table,
24262306a36Sopenharmony_ci};
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic const struct tsl2772_settings tsl2772_default_settings = {
24562306a36Sopenharmony_ci	.als_time = 255, /* 2.72 / 2.73 ms */
24662306a36Sopenharmony_ci	.als_gain = 0,
24762306a36Sopenharmony_ci	.prox_time = 255, /* 2.72 / 2.73 ms */
24862306a36Sopenharmony_ci	.prox_gain = 0,
24962306a36Sopenharmony_ci	.wait_time = 255,
25062306a36Sopenharmony_ci	.als_prox_config = 0,
25162306a36Sopenharmony_ci	.als_gain_trim = 1000,
25262306a36Sopenharmony_ci	.als_cal_target = 150,
25362306a36Sopenharmony_ci	.als_persistence = 1,
25462306a36Sopenharmony_ci	.als_interrupt_en = false,
25562306a36Sopenharmony_ci	.als_thresh_low = 200,
25662306a36Sopenharmony_ci	.als_thresh_high = 256,
25762306a36Sopenharmony_ci	.prox_persistence = 1,
25862306a36Sopenharmony_ci	.prox_interrupt_en = false,
25962306a36Sopenharmony_ci	.prox_thres_low  = 0,
26062306a36Sopenharmony_ci	.prox_thres_high = 512,
26162306a36Sopenharmony_ci	.prox_max_samples_cal = 30,
26262306a36Sopenharmony_ci	.prox_pulse_count = 8,
26362306a36Sopenharmony_ci	.prox_diode = TSL2772_DIODE1,
26462306a36Sopenharmony_ci	.prox_power = TSL2772_100_mA
26562306a36Sopenharmony_ci};
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic const s16 tsl2772_als_gain[] = {
26862306a36Sopenharmony_ci	1,
26962306a36Sopenharmony_ci	8,
27062306a36Sopenharmony_ci	16,
27162306a36Sopenharmony_ci	120
27262306a36Sopenharmony_ci};
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic const s16 tsl2772_prox_gain[] = {
27562306a36Sopenharmony_ci	1,
27662306a36Sopenharmony_ci	2,
27762306a36Sopenharmony_ci	4,
27862306a36Sopenharmony_ci	8
27962306a36Sopenharmony_ci};
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic const int tsl2772_int_time_avail[][6] = {
28262306a36Sopenharmony_ci	[tsl2571] = { 0, 2720, 0, 2720, 0, 696000 },
28362306a36Sopenharmony_ci	[tsl2671] = { 0, 2720, 0, 2720, 0, 696000 },
28462306a36Sopenharmony_ci	[tmd2671] = { 0, 2720, 0, 2720, 0, 696000 },
28562306a36Sopenharmony_ci	[tsl2771] = { 0, 2720, 0, 2720, 0, 696000 },
28662306a36Sopenharmony_ci	[tmd2771] = { 0, 2720, 0, 2720, 0, 696000 },
28762306a36Sopenharmony_ci	[tsl2572] = { 0, 2730, 0, 2730, 0, 699000 },
28862306a36Sopenharmony_ci	[tsl2672] = { 0, 2730, 0, 2730, 0, 699000 },
28962306a36Sopenharmony_ci	[tmd2672] = { 0, 2730, 0, 2730, 0, 699000 },
29062306a36Sopenharmony_ci	[tsl2772] = { 0, 2730, 0, 2730, 0, 699000 },
29162306a36Sopenharmony_ci	[tmd2772] = { 0, 2730, 0, 2730, 0, 699000 },
29262306a36Sopenharmony_ci	[apds9930] = { 0, 2730, 0, 2730, 0, 699000 },
29362306a36Sopenharmony_ci};
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic int tsl2772_int_calibscale_avail[] = { 1, 8, 16, 120 };
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic int tsl2772_prox_calibscale_avail[] = { 1, 2, 4, 8 };
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci/* Channel variations */
30062306a36Sopenharmony_cienum {
30162306a36Sopenharmony_ci	ALS,
30262306a36Sopenharmony_ci	PRX,
30362306a36Sopenharmony_ci	ALSPRX,
30462306a36Sopenharmony_ci	PRX2,
30562306a36Sopenharmony_ci	ALSPRX2,
30662306a36Sopenharmony_ci};
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic const u8 device_channel_config[] = {
30962306a36Sopenharmony_ci	[tsl2571] = ALS,
31062306a36Sopenharmony_ci	[tsl2671] = PRX,
31162306a36Sopenharmony_ci	[tmd2671] = PRX,
31262306a36Sopenharmony_ci	[tsl2771] = ALSPRX,
31362306a36Sopenharmony_ci	[tmd2771] = ALSPRX,
31462306a36Sopenharmony_ci	[tsl2572] = ALS,
31562306a36Sopenharmony_ci	[tsl2672] = PRX2,
31662306a36Sopenharmony_ci	[tmd2672] = PRX2,
31762306a36Sopenharmony_ci	[tsl2772] = ALSPRX2,
31862306a36Sopenharmony_ci	[tmd2772] = ALSPRX2,
31962306a36Sopenharmony_ci	[apds9930] = ALSPRX2,
32062306a36Sopenharmony_ci};
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic int tsl2772_read_status(struct tsl2772_chip *chip)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	int ret;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	ret = i2c_smbus_read_byte_data(chip->client,
32762306a36Sopenharmony_ci				       TSL2772_CMD_REG | TSL2772_STATUS);
32862306a36Sopenharmony_ci	if (ret < 0)
32962306a36Sopenharmony_ci		dev_err(&chip->client->dev,
33062306a36Sopenharmony_ci			"%s: failed to read STATUS register: %d\n", __func__,
33162306a36Sopenharmony_ci			ret);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	return ret;
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic int tsl2772_write_control_reg(struct tsl2772_chip *chip, u8 data)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	int ret;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	ret = i2c_smbus_write_byte_data(chip->client,
34162306a36Sopenharmony_ci					TSL2772_CMD_REG | TSL2772_CNTRL, data);
34262306a36Sopenharmony_ci	if (ret < 0) {
34362306a36Sopenharmony_ci		dev_err(&chip->client->dev,
34462306a36Sopenharmony_ci			"%s: failed to write to control register %x: %d\n",
34562306a36Sopenharmony_ci			__func__, data, ret);
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	return ret;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic int tsl2772_read_autoinc_regs(struct tsl2772_chip *chip, int lower_reg,
35262306a36Sopenharmony_ci				     int upper_reg)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	u8 buf[2];
35562306a36Sopenharmony_ci	int ret;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	ret = i2c_smbus_write_byte(chip->client,
35862306a36Sopenharmony_ci				   TSL2772_CMD_REG | TSL2772_CMD_AUTOINC_PROTO |
35962306a36Sopenharmony_ci				   lower_reg);
36062306a36Sopenharmony_ci	if (ret < 0) {
36162306a36Sopenharmony_ci		dev_err(&chip->client->dev,
36262306a36Sopenharmony_ci			"%s: failed to enable auto increment protocol: %d\n",
36362306a36Sopenharmony_ci			__func__, ret);
36462306a36Sopenharmony_ci		return ret;
36562306a36Sopenharmony_ci	}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	ret = i2c_smbus_read_byte_data(chip->client,
36862306a36Sopenharmony_ci				       TSL2772_CMD_REG | lower_reg);
36962306a36Sopenharmony_ci	if (ret < 0) {
37062306a36Sopenharmony_ci		dev_err(&chip->client->dev,
37162306a36Sopenharmony_ci			"%s: failed to read from register %x: %d\n", __func__,
37262306a36Sopenharmony_ci			lower_reg, ret);
37362306a36Sopenharmony_ci		return ret;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci	buf[0] = ret;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	ret = i2c_smbus_read_byte_data(chip->client,
37862306a36Sopenharmony_ci				       TSL2772_CMD_REG | upper_reg);
37962306a36Sopenharmony_ci	if (ret < 0) {
38062306a36Sopenharmony_ci		dev_err(&chip->client->dev,
38162306a36Sopenharmony_ci			"%s: failed to read from register %x: %d\n", __func__,
38262306a36Sopenharmony_ci			upper_reg, ret);
38362306a36Sopenharmony_ci		return ret;
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci	buf[1] = ret;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	ret = i2c_smbus_write_byte(chip->client,
38862306a36Sopenharmony_ci				   TSL2772_CMD_REG | TSL2772_CMD_REPEAT_PROTO |
38962306a36Sopenharmony_ci				   lower_reg);
39062306a36Sopenharmony_ci	if (ret < 0) {
39162306a36Sopenharmony_ci		dev_err(&chip->client->dev,
39262306a36Sopenharmony_ci			"%s: failed to enable repeated byte protocol: %d\n",
39362306a36Sopenharmony_ci			__func__, ret);
39462306a36Sopenharmony_ci		return ret;
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	return le16_to_cpup((const __le16 *)&buf[0]);
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci/**
40162306a36Sopenharmony_ci * tsl2772_get_lux() - Reads and calculates current lux value.
40262306a36Sopenharmony_ci * @indio_dev:	pointer to IIO device
40362306a36Sopenharmony_ci *
40462306a36Sopenharmony_ci * The raw ch0 and ch1 values of the ambient light sensed in the last
40562306a36Sopenharmony_ci * integration cycle are read from the device. The raw values are multiplied
40662306a36Sopenharmony_ci * by a device-specific scale factor, and divided by the integration time and
40762306a36Sopenharmony_ci * device gain. The code supports multiple lux equations through the lux table
40862306a36Sopenharmony_ci * coefficients. A lux gain trim is applied to each lux equation, and then the
40962306a36Sopenharmony_ci * maximum lux within the interval 0..65535 is selected.
41062306a36Sopenharmony_ci */
41162306a36Sopenharmony_cistatic int tsl2772_get_lux(struct iio_dev *indio_dev)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	struct tsl2772_chip *chip = iio_priv(indio_dev);
41462306a36Sopenharmony_ci	struct tsl2772_lux *p;
41562306a36Sopenharmony_ci	int max_lux, ret;
41662306a36Sopenharmony_ci	bool overflow;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	mutex_lock(&chip->als_mutex);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	if (chip->tsl2772_chip_status != TSL2772_CHIP_WORKING) {
42162306a36Sopenharmony_ci		dev_err(&chip->client->dev, "%s: device is not enabled\n",
42262306a36Sopenharmony_ci			__func__);
42362306a36Sopenharmony_ci		ret = -EBUSY;
42462306a36Sopenharmony_ci		goto out_unlock;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	ret = tsl2772_read_status(chip);
42862306a36Sopenharmony_ci	if (ret < 0)
42962306a36Sopenharmony_ci		goto out_unlock;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	if (!(ret & TSL2772_STA_ADC_VALID)) {
43262306a36Sopenharmony_ci		dev_err(&chip->client->dev,
43362306a36Sopenharmony_ci			"%s: data not valid yet\n", __func__);
43462306a36Sopenharmony_ci		ret = chip->als_cur_info.lux; /* return LAST VALUE */
43562306a36Sopenharmony_ci		goto out_unlock;
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	ret = tsl2772_read_autoinc_regs(chip, TSL2772_ALS_CHAN0LO,
43962306a36Sopenharmony_ci					TSL2772_ALS_CHAN0HI);
44062306a36Sopenharmony_ci	if (ret < 0)
44162306a36Sopenharmony_ci		goto out_unlock;
44262306a36Sopenharmony_ci	chip->als_cur_info.als_ch0 = ret;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	ret = tsl2772_read_autoinc_regs(chip, TSL2772_ALS_CHAN1LO,
44562306a36Sopenharmony_ci					TSL2772_ALS_CHAN1HI);
44662306a36Sopenharmony_ci	if (ret < 0)
44762306a36Sopenharmony_ci		goto out_unlock;
44862306a36Sopenharmony_ci	chip->als_cur_info.als_ch1 = ret;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	if (chip->als_cur_info.als_ch0 >= chip->als_saturation) {
45162306a36Sopenharmony_ci		max_lux = TSL2772_LUX_CALC_OVER_FLOW;
45262306a36Sopenharmony_ci		goto update_struct_with_max_lux;
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	if (!chip->als_cur_info.als_ch0) {
45662306a36Sopenharmony_ci		/* have no data, so return LAST VALUE */
45762306a36Sopenharmony_ci		ret = chip->als_cur_info.lux;
45862306a36Sopenharmony_ci		goto out_unlock;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	max_lux = 0;
46262306a36Sopenharmony_ci	overflow = false;
46362306a36Sopenharmony_ci	for (p = (struct tsl2772_lux *)chip->tsl2772_device_lux; p->ch0 != 0;
46462306a36Sopenharmony_ci	     p++) {
46562306a36Sopenharmony_ci		int lux;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci		lux = ((chip->als_cur_info.als_ch0 * p->ch0) -
46862306a36Sopenharmony_ci		       (chip->als_cur_info.als_ch1 * p->ch1)) /
46962306a36Sopenharmony_ci			chip->als_gain_time_scale;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci		/*
47262306a36Sopenharmony_ci		 * The als_gain_trim can have a value within the range 250..4000
47362306a36Sopenharmony_ci		 * and is a multiplier for the lux. A trim of 1000 makes no
47462306a36Sopenharmony_ci		 * changes to the lux, less than 1000 scales it down, and
47562306a36Sopenharmony_ci		 * greater than 1000 scales it up.
47662306a36Sopenharmony_ci		 */
47762306a36Sopenharmony_ci		lux = (lux * chip->settings.als_gain_trim) / 1000;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci		if (lux > TSL2772_LUX_CALC_OVER_FLOW) {
48062306a36Sopenharmony_ci			overflow = true;
48162306a36Sopenharmony_ci			continue;
48262306a36Sopenharmony_ci		}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci		max_lux = max(max_lux, lux);
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	if (overflow && max_lux == 0)
48862306a36Sopenharmony_ci		max_lux = TSL2772_LUX_CALC_OVER_FLOW;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ciupdate_struct_with_max_lux:
49162306a36Sopenharmony_ci	chip->als_cur_info.lux = max_lux;
49262306a36Sopenharmony_ci	ret = max_lux;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ciout_unlock:
49562306a36Sopenharmony_ci	mutex_unlock(&chip->als_mutex);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	return ret;
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci/**
50162306a36Sopenharmony_ci * tsl2772_get_prox() - Reads proximity data registers and updates
50262306a36Sopenharmony_ci *                      chip->prox_data.
50362306a36Sopenharmony_ci *
50462306a36Sopenharmony_ci * @indio_dev:	pointer to IIO device
50562306a36Sopenharmony_ci */
50662306a36Sopenharmony_cistatic int tsl2772_get_prox(struct iio_dev *indio_dev)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	struct tsl2772_chip *chip = iio_priv(indio_dev);
50962306a36Sopenharmony_ci	int ret;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	mutex_lock(&chip->prox_mutex);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	ret = tsl2772_read_status(chip);
51462306a36Sopenharmony_ci	if (ret < 0)
51562306a36Sopenharmony_ci		goto prox_poll_err;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	switch (chip->id) {
51862306a36Sopenharmony_ci	case tsl2571:
51962306a36Sopenharmony_ci	case tsl2671:
52062306a36Sopenharmony_ci	case tmd2671:
52162306a36Sopenharmony_ci	case tsl2771:
52262306a36Sopenharmony_ci	case tmd2771:
52362306a36Sopenharmony_ci		if (!(ret & TSL2772_STA_ADC_VALID)) {
52462306a36Sopenharmony_ci			ret = -EINVAL;
52562306a36Sopenharmony_ci			goto prox_poll_err;
52662306a36Sopenharmony_ci		}
52762306a36Sopenharmony_ci		break;
52862306a36Sopenharmony_ci	case tsl2572:
52962306a36Sopenharmony_ci	case tsl2672:
53062306a36Sopenharmony_ci	case tmd2672:
53162306a36Sopenharmony_ci	case tsl2772:
53262306a36Sopenharmony_ci	case tmd2772:
53362306a36Sopenharmony_ci	case apds9930:
53462306a36Sopenharmony_ci		if (!(ret & TSL2772_STA_PRX_VALID)) {
53562306a36Sopenharmony_ci			ret = -EINVAL;
53662306a36Sopenharmony_ci			goto prox_poll_err;
53762306a36Sopenharmony_ci		}
53862306a36Sopenharmony_ci		break;
53962306a36Sopenharmony_ci	}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	ret = tsl2772_read_autoinc_regs(chip, TSL2772_PRX_LO, TSL2772_PRX_HI);
54262306a36Sopenharmony_ci	if (ret < 0)
54362306a36Sopenharmony_ci		goto prox_poll_err;
54462306a36Sopenharmony_ci	chip->prox_data = ret;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ciprox_poll_err:
54762306a36Sopenharmony_ci	mutex_unlock(&chip->prox_mutex);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	return ret;
55062306a36Sopenharmony_ci}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_cistatic int tsl2772_read_prox_led_current(struct tsl2772_chip *chip)
55362306a36Sopenharmony_ci{
55462306a36Sopenharmony_ci	struct device *dev = &chip->client->dev;
55562306a36Sopenharmony_ci	int ret, tmp, i;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	ret = device_property_read_u32(dev, "led-max-microamp", &tmp);
55862306a36Sopenharmony_ci	if (ret < 0)
55962306a36Sopenharmony_ci		return ret;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	for (i = 0; tsl2772_led_currents[i][0] != 0; i++) {
56262306a36Sopenharmony_ci		if (tmp == tsl2772_led_currents[i][0]) {
56362306a36Sopenharmony_ci			chip->settings.prox_power = tsl2772_led_currents[i][1];
56462306a36Sopenharmony_ci			return 0;
56562306a36Sopenharmony_ci		}
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	dev_err(dev, "Invalid value %d for led-max-microamp\n", tmp);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	return -EINVAL;
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic int tsl2772_read_prox_diodes(struct tsl2772_chip *chip)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	struct device *dev = &chip->client->dev;
57662306a36Sopenharmony_ci	int i, ret, num_leds, prox_diode_mask;
57762306a36Sopenharmony_ci	u32 leds[TSL2772_MAX_PROX_LEDS];
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	ret = device_property_count_u32(dev, "amstaos,proximity-diodes");
58062306a36Sopenharmony_ci	if (ret < 0)
58162306a36Sopenharmony_ci		return ret;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	num_leds = ret;
58462306a36Sopenharmony_ci	if (num_leds > TSL2772_MAX_PROX_LEDS)
58562306a36Sopenharmony_ci		num_leds = TSL2772_MAX_PROX_LEDS;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	ret = device_property_read_u32_array(dev, "amstaos,proximity-diodes", leds, num_leds);
58862306a36Sopenharmony_ci	if (ret < 0) {
58962306a36Sopenharmony_ci		dev_err(dev, "Invalid value for amstaos,proximity-diodes: %d.\n", ret);
59062306a36Sopenharmony_ci		return ret;
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	prox_diode_mask = 0;
59462306a36Sopenharmony_ci	for (i = 0; i < num_leds; i++) {
59562306a36Sopenharmony_ci		if (leds[i] == 0)
59662306a36Sopenharmony_ci			prox_diode_mask |= TSL2772_DIODE0;
59762306a36Sopenharmony_ci		else if (leds[i] == 1)
59862306a36Sopenharmony_ci			prox_diode_mask |= TSL2772_DIODE1;
59962306a36Sopenharmony_ci		else {
60062306a36Sopenharmony_ci			dev_err(dev, "Invalid value %d in amstaos,proximity-diodes.\n", leds[i]);
60162306a36Sopenharmony_ci			return -EINVAL;
60262306a36Sopenharmony_ci		}
60362306a36Sopenharmony_ci	}
60462306a36Sopenharmony_ci	chip->settings.prox_diode = prox_diode_mask;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	return 0;
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic void tsl2772_parse_dt(struct tsl2772_chip *chip)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	tsl2772_read_prox_led_current(chip);
61262306a36Sopenharmony_ci	tsl2772_read_prox_diodes(chip);
61362306a36Sopenharmony_ci}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci/**
61662306a36Sopenharmony_ci * tsl2772_defaults() - Populates the device nominal operating parameters
61762306a36Sopenharmony_ci *                      with those provided by a 'platform' data struct or
61862306a36Sopenharmony_ci *                      with prefined defaults.
61962306a36Sopenharmony_ci *
62062306a36Sopenharmony_ci * @chip:               pointer to device structure.
62162306a36Sopenharmony_ci */
62262306a36Sopenharmony_cistatic void tsl2772_defaults(struct tsl2772_chip *chip)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	/* If Operational settings defined elsewhere.. */
62562306a36Sopenharmony_ci	if (chip->pdata && chip->pdata->platform_default_settings)
62662306a36Sopenharmony_ci		memcpy(&chip->settings, chip->pdata->platform_default_settings,
62762306a36Sopenharmony_ci		       sizeof(tsl2772_default_settings));
62862306a36Sopenharmony_ci	else
62962306a36Sopenharmony_ci		memcpy(&chip->settings, &tsl2772_default_settings,
63062306a36Sopenharmony_ci		       sizeof(tsl2772_default_settings));
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	/* Load up the proper lux table. */
63362306a36Sopenharmony_ci	if (chip->pdata && chip->pdata->platform_lux_table[0].ch0 != 0)
63462306a36Sopenharmony_ci		memcpy(chip->tsl2772_device_lux,
63562306a36Sopenharmony_ci		       chip->pdata->platform_lux_table,
63662306a36Sopenharmony_ci		       sizeof(chip->pdata->platform_lux_table));
63762306a36Sopenharmony_ci	else
63862306a36Sopenharmony_ci		memcpy(chip->tsl2772_device_lux,
63962306a36Sopenharmony_ci		       tsl2772_default_lux_table_group[chip->id],
64062306a36Sopenharmony_ci		       TSL2772_DEFAULT_TABLE_BYTES);
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	tsl2772_parse_dt(chip);
64362306a36Sopenharmony_ci}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci/**
64662306a36Sopenharmony_ci * tsl2772_als_calibrate() -	Obtain single reading and calculate
64762306a36Sopenharmony_ci *                              the als_gain_trim.
64862306a36Sopenharmony_ci *
64962306a36Sopenharmony_ci * @indio_dev:	pointer to IIO device
65062306a36Sopenharmony_ci */
65162306a36Sopenharmony_cistatic int tsl2772_als_calibrate(struct iio_dev *indio_dev)
65262306a36Sopenharmony_ci{
65362306a36Sopenharmony_ci	struct tsl2772_chip *chip = iio_priv(indio_dev);
65462306a36Sopenharmony_ci	int ret, lux_val;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	ret = i2c_smbus_read_byte_data(chip->client,
65762306a36Sopenharmony_ci				       TSL2772_CMD_REG | TSL2772_CNTRL);
65862306a36Sopenharmony_ci	if (ret < 0) {
65962306a36Sopenharmony_ci		dev_err(&chip->client->dev,
66062306a36Sopenharmony_ci			"%s: failed to read from the CNTRL register\n",
66162306a36Sopenharmony_ci			__func__);
66262306a36Sopenharmony_ci		return ret;
66362306a36Sopenharmony_ci	}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	if ((ret & (TSL2772_CNTL_ADC_ENBL | TSL2772_CNTL_PWR_ON))
66662306a36Sopenharmony_ci			!= (TSL2772_CNTL_ADC_ENBL | TSL2772_CNTL_PWR_ON)) {
66762306a36Sopenharmony_ci		dev_err(&chip->client->dev,
66862306a36Sopenharmony_ci			"%s: Device is not powered on and/or ADC is not enabled\n",
66962306a36Sopenharmony_ci			__func__);
67062306a36Sopenharmony_ci		return -EINVAL;
67162306a36Sopenharmony_ci	} else if ((ret & TSL2772_STA_ADC_VALID) != TSL2772_STA_ADC_VALID) {
67262306a36Sopenharmony_ci		dev_err(&chip->client->dev,
67362306a36Sopenharmony_ci			"%s: The two ADC channels have not completed an integration cycle\n",
67462306a36Sopenharmony_ci			__func__);
67562306a36Sopenharmony_ci		return -ENODATA;
67662306a36Sopenharmony_ci	}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	lux_val = tsl2772_get_lux(indio_dev);
67962306a36Sopenharmony_ci	if (lux_val < 0) {
68062306a36Sopenharmony_ci		dev_err(&chip->client->dev,
68162306a36Sopenharmony_ci			"%s: failed to get lux\n", __func__);
68262306a36Sopenharmony_ci		return lux_val;
68362306a36Sopenharmony_ci	}
68462306a36Sopenharmony_ci	if (lux_val == 0)
68562306a36Sopenharmony_ci		return -ERANGE;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	ret = (chip->settings.als_cal_target * chip->settings.als_gain_trim) /
68862306a36Sopenharmony_ci			lux_val;
68962306a36Sopenharmony_ci	if (ret < TSL2772_ALS_GAIN_TRIM_MIN || ret > TSL2772_ALS_GAIN_TRIM_MAX)
69062306a36Sopenharmony_ci		return -ERANGE;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	chip->settings.als_gain_trim = ret;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	return ret;
69562306a36Sopenharmony_ci}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_cistatic void tsl2772_disable_regulators_action(void *_data)
69862306a36Sopenharmony_ci{
69962306a36Sopenharmony_ci	struct tsl2772_chip *chip = _data;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	regulator_bulk_disable(ARRAY_SIZE(chip->supplies), chip->supplies);
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic int tsl2772_chip_on(struct iio_dev *indio_dev)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	struct tsl2772_chip *chip = iio_priv(indio_dev);
70762306a36Sopenharmony_ci	int ret, i, als_count, als_time_us;
70862306a36Sopenharmony_ci	u8 *dev_reg, reg_val;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	/* Non calculated parameters */
71162306a36Sopenharmony_ci	chip->tsl2772_config[TSL2772_ALS_TIME] = chip->settings.als_time;
71262306a36Sopenharmony_ci	chip->tsl2772_config[TSL2772_PRX_TIME] = chip->settings.prox_time;
71362306a36Sopenharmony_ci	chip->tsl2772_config[TSL2772_WAIT_TIME] = chip->settings.wait_time;
71462306a36Sopenharmony_ci	chip->tsl2772_config[TSL2772_ALS_PRX_CONFIG] =
71562306a36Sopenharmony_ci		chip->settings.als_prox_config;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	chip->tsl2772_config[TSL2772_ALS_MINTHRESHLO] =
71862306a36Sopenharmony_ci		(chip->settings.als_thresh_low) & 0xFF;
71962306a36Sopenharmony_ci	chip->tsl2772_config[TSL2772_ALS_MINTHRESHHI] =
72062306a36Sopenharmony_ci		(chip->settings.als_thresh_low >> 8) & 0xFF;
72162306a36Sopenharmony_ci	chip->tsl2772_config[TSL2772_ALS_MAXTHRESHLO] =
72262306a36Sopenharmony_ci		(chip->settings.als_thresh_high) & 0xFF;
72362306a36Sopenharmony_ci	chip->tsl2772_config[TSL2772_ALS_MAXTHRESHHI] =
72462306a36Sopenharmony_ci		(chip->settings.als_thresh_high >> 8) & 0xFF;
72562306a36Sopenharmony_ci	chip->tsl2772_config[TSL2772_PERSISTENCE] =
72662306a36Sopenharmony_ci		(chip->settings.prox_persistence & 0xFF) << 4 |
72762306a36Sopenharmony_ci		(chip->settings.als_persistence & 0xFF);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	chip->tsl2772_config[TSL2772_PRX_COUNT] =
73062306a36Sopenharmony_ci			chip->settings.prox_pulse_count;
73162306a36Sopenharmony_ci	chip->tsl2772_config[TSL2772_PRX_MINTHRESHLO] =
73262306a36Sopenharmony_ci			(chip->settings.prox_thres_low) & 0xFF;
73362306a36Sopenharmony_ci	chip->tsl2772_config[TSL2772_PRX_MINTHRESHHI] =
73462306a36Sopenharmony_ci			(chip->settings.prox_thres_low >> 8) & 0xFF;
73562306a36Sopenharmony_ci	chip->tsl2772_config[TSL2772_PRX_MAXTHRESHLO] =
73662306a36Sopenharmony_ci			(chip->settings.prox_thres_high) & 0xFF;
73762306a36Sopenharmony_ci	chip->tsl2772_config[TSL2772_PRX_MAXTHRESHHI] =
73862306a36Sopenharmony_ci			(chip->settings.prox_thres_high >> 8) & 0xFF;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	/* and make sure we're not already on */
74162306a36Sopenharmony_ci	if (chip->tsl2772_chip_status == TSL2772_CHIP_WORKING) {
74262306a36Sopenharmony_ci		/* if forcing a register update - turn off, then on */
74362306a36Sopenharmony_ci		dev_info(&chip->client->dev, "device is already enabled\n");
74462306a36Sopenharmony_ci		return -EINVAL;
74562306a36Sopenharmony_ci	}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	/* Set the gain based on tsl2772_settings struct */
74862306a36Sopenharmony_ci	chip->tsl2772_config[TSL2772_GAIN] =
74962306a36Sopenharmony_ci		(chip->settings.als_gain & 0xFF) |
75062306a36Sopenharmony_ci		((chip->settings.prox_gain & 0xFF) << 2) |
75162306a36Sopenharmony_ci		(chip->settings.prox_diode << 4) |
75262306a36Sopenharmony_ci		(chip->settings.prox_power << 6);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	/* set chip time scaling and saturation */
75562306a36Sopenharmony_ci	als_count = 256 - chip->settings.als_time;
75662306a36Sopenharmony_ci	als_time_us = als_count * tsl2772_int_time_avail[chip->id][3];
75762306a36Sopenharmony_ci	chip->als_saturation = als_count * 768; /* 75% of full scale */
75862306a36Sopenharmony_ci	chip->als_gain_time_scale = als_time_us *
75962306a36Sopenharmony_ci		tsl2772_als_gain[chip->settings.als_gain];
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	/*
76262306a36Sopenharmony_ci	 * TSL2772 Specific power-on / adc enable sequence
76362306a36Sopenharmony_ci	 * Power on the device 1st.
76462306a36Sopenharmony_ci	 */
76562306a36Sopenharmony_ci	ret = tsl2772_write_control_reg(chip, TSL2772_CNTL_PWR_ON);
76662306a36Sopenharmony_ci	if (ret < 0)
76762306a36Sopenharmony_ci		return ret;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	/*
77062306a36Sopenharmony_ci	 * Use the following shadow copy for our delay before enabling ADC.
77162306a36Sopenharmony_ci	 * Write all the registers.
77262306a36Sopenharmony_ci	 */
77362306a36Sopenharmony_ci	for (i = 0, dev_reg = chip->tsl2772_config;
77462306a36Sopenharmony_ci			i < TSL2772_MAX_CONFIG_REG; i++) {
77562306a36Sopenharmony_ci		int reg = TSL2772_CMD_REG + i;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci		ret = i2c_smbus_write_byte_data(chip->client, reg,
77862306a36Sopenharmony_ci						*dev_reg++);
77962306a36Sopenharmony_ci		if (ret < 0) {
78062306a36Sopenharmony_ci			dev_err(&chip->client->dev,
78162306a36Sopenharmony_ci				"%s: failed to write to register %x: %d\n",
78262306a36Sopenharmony_ci				__func__, reg, ret);
78362306a36Sopenharmony_ci			return ret;
78462306a36Sopenharmony_ci		}
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	/* Power-on settling time */
78862306a36Sopenharmony_ci	usleep_range(3000, 3500);
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	reg_val = TSL2772_CNTL_PWR_ON | TSL2772_CNTL_ADC_ENBL |
79162306a36Sopenharmony_ci		  TSL2772_CNTL_PROX_DET_ENBL;
79262306a36Sopenharmony_ci	if (chip->settings.als_interrupt_en)
79362306a36Sopenharmony_ci		reg_val |= TSL2772_CNTL_ALS_INT_ENBL;
79462306a36Sopenharmony_ci	if (chip->settings.prox_interrupt_en)
79562306a36Sopenharmony_ci		reg_val |= TSL2772_CNTL_PROX_INT_ENBL;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	ret = tsl2772_write_control_reg(chip, reg_val);
79862306a36Sopenharmony_ci	if (ret < 0)
79962306a36Sopenharmony_ci		return ret;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	ret = i2c_smbus_write_byte(chip->client,
80262306a36Sopenharmony_ci				   TSL2772_CMD_REG | TSL2772_CMD_SPL_FN |
80362306a36Sopenharmony_ci				   TSL2772_CMD_PROXALS_INT_CLR);
80462306a36Sopenharmony_ci	if (ret < 0) {
80562306a36Sopenharmony_ci		dev_err(&chip->client->dev,
80662306a36Sopenharmony_ci			"%s: failed to clear interrupt status: %d\n",
80762306a36Sopenharmony_ci			__func__, ret);
80862306a36Sopenharmony_ci		return ret;
80962306a36Sopenharmony_ci	}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	chip->tsl2772_chip_status = TSL2772_CHIP_WORKING;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	return ret;
81462306a36Sopenharmony_ci}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_cistatic int tsl2772_chip_off(struct iio_dev *indio_dev)
81762306a36Sopenharmony_ci{
81862306a36Sopenharmony_ci	struct tsl2772_chip *chip = iio_priv(indio_dev);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	/* turn device off */
82162306a36Sopenharmony_ci	chip->tsl2772_chip_status = TSL2772_CHIP_SUSPENDED;
82262306a36Sopenharmony_ci	return tsl2772_write_control_reg(chip, 0x00);
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_cistatic void tsl2772_chip_off_action(void *data)
82662306a36Sopenharmony_ci{
82762306a36Sopenharmony_ci	struct iio_dev *indio_dev = data;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	tsl2772_chip_off(indio_dev);
83062306a36Sopenharmony_ci}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci/**
83362306a36Sopenharmony_ci * tsl2772_invoke_change - power cycle the device to implement the user
83462306a36Sopenharmony_ci *                         parameters
83562306a36Sopenharmony_ci * @indio_dev:	pointer to IIO device
83662306a36Sopenharmony_ci *
83762306a36Sopenharmony_ci * Obtain and lock both ALS and PROX resources, determine and save device state
83862306a36Sopenharmony_ci * (On/Off), cycle device to implement updated parameter, put device back into
83962306a36Sopenharmony_ci * proper state, and unlock resource.
84062306a36Sopenharmony_ci */
84162306a36Sopenharmony_cistatic int tsl2772_invoke_change(struct iio_dev *indio_dev)
84262306a36Sopenharmony_ci{
84362306a36Sopenharmony_ci	struct tsl2772_chip *chip = iio_priv(indio_dev);
84462306a36Sopenharmony_ci	int device_status = chip->tsl2772_chip_status;
84562306a36Sopenharmony_ci	int ret;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	mutex_lock(&chip->als_mutex);
84862306a36Sopenharmony_ci	mutex_lock(&chip->prox_mutex);
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	if (device_status == TSL2772_CHIP_WORKING) {
85162306a36Sopenharmony_ci		ret = tsl2772_chip_off(indio_dev);
85262306a36Sopenharmony_ci		if (ret < 0)
85362306a36Sopenharmony_ci			goto unlock;
85462306a36Sopenharmony_ci	}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	ret = tsl2772_chip_on(indio_dev);
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ciunlock:
85962306a36Sopenharmony_ci	mutex_unlock(&chip->prox_mutex);
86062306a36Sopenharmony_ci	mutex_unlock(&chip->als_mutex);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	return ret;
86362306a36Sopenharmony_ci}
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_cistatic int tsl2772_prox_cal(struct iio_dev *indio_dev)
86662306a36Sopenharmony_ci{
86762306a36Sopenharmony_ci	struct tsl2772_chip *chip = iio_priv(indio_dev);
86862306a36Sopenharmony_ci	int prox_history[MAX_SAMPLES_CAL + 1];
86962306a36Sopenharmony_ci	int i, ret, mean, max, sample_sum;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	if (chip->settings.prox_max_samples_cal < 1 ||
87262306a36Sopenharmony_ci	    chip->settings.prox_max_samples_cal > MAX_SAMPLES_CAL)
87362306a36Sopenharmony_ci		return -EINVAL;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	for (i = 0; i < chip->settings.prox_max_samples_cal; i++) {
87662306a36Sopenharmony_ci		usleep_range(15000, 17500);
87762306a36Sopenharmony_ci		ret = tsl2772_get_prox(indio_dev);
87862306a36Sopenharmony_ci		if (ret < 0)
87962306a36Sopenharmony_ci			return ret;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci		prox_history[i] = chip->prox_data;
88262306a36Sopenharmony_ci	}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	sample_sum = 0;
88562306a36Sopenharmony_ci	max = INT_MIN;
88662306a36Sopenharmony_ci	for (i = 0; i < chip->settings.prox_max_samples_cal; i++) {
88762306a36Sopenharmony_ci		sample_sum += prox_history[i];
88862306a36Sopenharmony_ci		max = max(max, prox_history[i]);
88962306a36Sopenharmony_ci	}
89062306a36Sopenharmony_ci	mean = sample_sum / chip->settings.prox_max_samples_cal;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	chip->settings.prox_thres_high = (max << 1) - mean;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	return tsl2772_invoke_change(indio_dev);
89562306a36Sopenharmony_ci}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_cistatic int tsl2772_read_avail(struct iio_dev *indio_dev,
89862306a36Sopenharmony_ci			      struct iio_chan_spec const *chan,
89962306a36Sopenharmony_ci			      const int **vals, int *type, int *length,
90062306a36Sopenharmony_ci			      long mask)
90162306a36Sopenharmony_ci{
90262306a36Sopenharmony_ci	struct tsl2772_chip *chip = iio_priv(indio_dev);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	switch (mask) {
90562306a36Sopenharmony_ci	case IIO_CHAN_INFO_CALIBSCALE:
90662306a36Sopenharmony_ci		if (chan->type == IIO_INTENSITY) {
90762306a36Sopenharmony_ci			*length = ARRAY_SIZE(tsl2772_int_calibscale_avail);
90862306a36Sopenharmony_ci			*vals = tsl2772_int_calibscale_avail;
90962306a36Sopenharmony_ci		} else {
91062306a36Sopenharmony_ci			*length = ARRAY_SIZE(tsl2772_prox_calibscale_avail);
91162306a36Sopenharmony_ci			*vals = tsl2772_prox_calibscale_avail;
91262306a36Sopenharmony_ci		}
91362306a36Sopenharmony_ci		*type = IIO_VAL_INT;
91462306a36Sopenharmony_ci		return IIO_AVAIL_LIST;
91562306a36Sopenharmony_ci	case IIO_CHAN_INFO_INT_TIME:
91662306a36Sopenharmony_ci		*length = ARRAY_SIZE(tsl2772_int_time_avail[chip->id]);
91762306a36Sopenharmony_ci		*vals = tsl2772_int_time_avail[chip->id];
91862306a36Sopenharmony_ci		*type = IIO_VAL_INT_PLUS_MICRO;
91962306a36Sopenharmony_ci		return IIO_AVAIL_RANGE;
92062306a36Sopenharmony_ci	}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	return -EINVAL;
92362306a36Sopenharmony_ci}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_cistatic ssize_t in_illuminance0_target_input_show(struct device *dev,
92662306a36Sopenharmony_ci						 struct device_attribute *attr,
92762306a36Sopenharmony_ci						 char *buf)
92862306a36Sopenharmony_ci{
92962306a36Sopenharmony_ci	struct tsl2772_chip *chip = iio_priv(dev_to_iio_dev(dev));
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%d\n", chip->settings.als_cal_target);
93262306a36Sopenharmony_ci}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_cistatic ssize_t in_illuminance0_target_input_store(struct device *dev,
93562306a36Sopenharmony_ci						  struct device_attribute *attr,
93662306a36Sopenharmony_ci						  const char *buf, size_t len)
93762306a36Sopenharmony_ci{
93862306a36Sopenharmony_ci	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
93962306a36Sopenharmony_ci	struct tsl2772_chip *chip = iio_priv(indio_dev);
94062306a36Sopenharmony_ci	u16 value;
94162306a36Sopenharmony_ci	int ret;
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	if (kstrtou16(buf, 0, &value))
94462306a36Sopenharmony_ci		return -EINVAL;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	chip->settings.als_cal_target = value;
94762306a36Sopenharmony_ci	ret = tsl2772_invoke_change(indio_dev);
94862306a36Sopenharmony_ci	if (ret < 0)
94962306a36Sopenharmony_ci		return ret;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	return len;
95262306a36Sopenharmony_ci}
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_cistatic ssize_t in_illuminance0_calibrate_store(struct device *dev,
95562306a36Sopenharmony_ci					       struct device_attribute *attr,
95662306a36Sopenharmony_ci					       const char *buf, size_t len)
95762306a36Sopenharmony_ci{
95862306a36Sopenharmony_ci	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
95962306a36Sopenharmony_ci	bool value;
96062306a36Sopenharmony_ci	int ret;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	if (kstrtobool(buf, &value) || !value)
96362306a36Sopenharmony_ci		return -EINVAL;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	ret = tsl2772_als_calibrate(indio_dev);
96662306a36Sopenharmony_ci	if (ret < 0)
96762306a36Sopenharmony_ci		return ret;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	ret = tsl2772_invoke_change(indio_dev);
97062306a36Sopenharmony_ci	if (ret < 0)
97162306a36Sopenharmony_ci		return ret;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	return len;
97462306a36Sopenharmony_ci}
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_cistatic ssize_t in_illuminance0_lux_table_show(struct device *dev,
97762306a36Sopenharmony_ci					      struct device_attribute *attr,
97862306a36Sopenharmony_ci					      char *buf)
97962306a36Sopenharmony_ci{
98062306a36Sopenharmony_ci	struct tsl2772_chip *chip = iio_priv(dev_to_iio_dev(dev));
98162306a36Sopenharmony_ci	int i = 0;
98262306a36Sopenharmony_ci	int offset = 0;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	while (i < TSL2772_MAX_LUX_TABLE_SIZE) {
98562306a36Sopenharmony_ci		offset += scnprintf(buf + offset, PAGE_SIZE - offset, "%u,%u,",
98662306a36Sopenharmony_ci			chip->tsl2772_device_lux[i].ch0,
98762306a36Sopenharmony_ci			chip->tsl2772_device_lux[i].ch1);
98862306a36Sopenharmony_ci		if (chip->tsl2772_device_lux[i].ch0 == 0) {
98962306a36Sopenharmony_ci			/*
99062306a36Sopenharmony_ci			 * We just printed the first "0" entry.
99162306a36Sopenharmony_ci			 * Now get rid of the extra "," and break.
99262306a36Sopenharmony_ci			 */
99362306a36Sopenharmony_ci			offset--;
99462306a36Sopenharmony_ci			break;
99562306a36Sopenharmony_ci		}
99662306a36Sopenharmony_ci		i++;
99762306a36Sopenharmony_ci	}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	offset += scnprintf(buf + offset, PAGE_SIZE - offset, "\n");
100062306a36Sopenharmony_ci	return offset;
100162306a36Sopenharmony_ci}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_cistatic ssize_t in_illuminance0_lux_table_store(struct device *dev,
100462306a36Sopenharmony_ci					       struct device_attribute *attr,
100562306a36Sopenharmony_ci					       const char *buf, size_t len)
100662306a36Sopenharmony_ci{
100762306a36Sopenharmony_ci	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
100862306a36Sopenharmony_ci	struct tsl2772_chip *chip = iio_priv(indio_dev);
100962306a36Sopenharmony_ci	int value[ARRAY_SIZE(chip->tsl2772_device_lux) * 2 + 1];
101062306a36Sopenharmony_ci	int n, ret;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	get_options(buf, ARRAY_SIZE(value), value);
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	/*
101562306a36Sopenharmony_ci	 * We now have an array of ints starting at value[1], and
101662306a36Sopenharmony_ci	 * enumerated by value[0].
101762306a36Sopenharmony_ci	 * We expect each group of two ints to be one table entry,
101862306a36Sopenharmony_ci	 * and the last table entry is all 0.
101962306a36Sopenharmony_ci	 */
102062306a36Sopenharmony_ci	n = value[0];
102162306a36Sopenharmony_ci	if ((n % 2) || n < 4 ||
102262306a36Sopenharmony_ci	    n > ((ARRAY_SIZE(chip->tsl2772_device_lux) - 1) * 2))
102362306a36Sopenharmony_ci		return -EINVAL;
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	if ((value[(n - 1)] | value[n]) != 0)
102662306a36Sopenharmony_ci		return -EINVAL;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	if (chip->tsl2772_chip_status == TSL2772_CHIP_WORKING) {
102962306a36Sopenharmony_ci		ret = tsl2772_chip_off(indio_dev);
103062306a36Sopenharmony_ci		if (ret < 0)
103162306a36Sopenharmony_ci			return ret;
103262306a36Sopenharmony_ci	}
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	/* Zero out the table */
103562306a36Sopenharmony_ci	memset(chip->tsl2772_device_lux, 0, sizeof(chip->tsl2772_device_lux));
103662306a36Sopenharmony_ci	memcpy(chip->tsl2772_device_lux, &value[1], (value[0] * 4));
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	ret = tsl2772_invoke_change(indio_dev);
103962306a36Sopenharmony_ci	if (ret < 0)
104062306a36Sopenharmony_ci		return ret;
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	return len;
104362306a36Sopenharmony_ci}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_cistatic ssize_t in_proximity0_calibrate_store(struct device *dev,
104662306a36Sopenharmony_ci					     struct device_attribute *attr,
104762306a36Sopenharmony_ci					     const char *buf, size_t len)
104862306a36Sopenharmony_ci{
104962306a36Sopenharmony_ci	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
105062306a36Sopenharmony_ci	bool value;
105162306a36Sopenharmony_ci	int ret;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	if (kstrtobool(buf, &value) || !value)
105462306a36Sopenharmony_ci		return -EINVAL;
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	ret = tsl2772_prox_cal(indio_dev);
105762306a36Sopenharmony_ci	if (ret < 0)
105862306a36Sopenharmony_ci		return ret;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	ret = tsl2772_invoke_change(indio_dev);
106162306a36Sopenharmony_ci	if (ret < 0)
106262306a36Sopenharmony_ci		return ret;
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	return len;
106562306a36Sopenharmony_ci}
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_cistatic int tsl2772_read_interrupt_config(struct iio_dev *indio_dev,
106862306a36Sopenharmony_ci					 const struct iio_chan_spec *chan,
106962306a36Sopenharmony_ci					 enum iio_event_type type,
107062306a36Sopenharmony_ci					 enum iio_event_direction dir)
107162306a36Sopenharmony_ci{
107262306a36Sopenharmony_ci	struct tsl2772_chip *chip = iio_priv(indio_dev);
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	if (chan->type == IIO_INTENSITY)
107562306a36Sopenharmony_ci		return chip->settings.als_interrupt_en;
107662306a36Sopenharmony_ci	else
107762306a36Sopenharmony_ci		return chip->settings.prox_interrupt_en;
107862306a36Sopenharmony_ci}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_cistatic int tsl2772_write_interrupt_config(struct iio_dev *indio_dev,
108162306a36Sopenharmony_ci					  const struct iio_chan_spec *chan,
108262306a36Sopenharmony_ci					  enum iio_event_type type,
108362306a36Sopenharmony_ci					  enum iio_event_direction dir,
108462306a36Sopenharmony_ci					  int val)
108562306a36Sopenharmony_ci{
108662306a36Sopenharmony_ci	struct tsl2772_chip *chip = iio_priv(indio_dev);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	if (chan->type == IIO_INTENSITY)
108962306a36Sopenharmony_ci		chip->settings.als_interrupt_en = val ? true : false;
109062306a36Sopenharmony_ci	else
109162306a36Sopenharmony_ci		chip->settings.prox_interrupt_en = val ? true : false;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	return tsl2772_invoke_change(indio_dev);
109462306a36Sopenharmony_ci}
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_cistatic int tsl2772_write_event_value(struct iio_dev *indio_dev,
109762306a36Sopenharmony_ci				     const struct iio_chan_spec *chan,
109862306a36Sopenharmony_ci				     enum iio_event_type type,
109962306a36Sopenharmony_ci				     enum iio_event_direction dir,
110062306a36Sopenharmony_ci				     enum iio_event_info info,
110162306a36Sopenharmony_ci				     int val, int val2)
110262306a36Sopenharmony_ci{
110362306a36Sopenharmony_ci	struct tsl2772_chip *chip = iio_priv(indio_dev);
110462306a36Sopenharmony_ci	int ret = -EINVAL, count, persistence;
110562306a36Sopenharmony_ci	u8 time;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	switch (info) {
110862306a36Sopenharmony_ci	case IIO_EV_INFO_VALUE:
110962306a36Sopenharmony_ci		if (chan->type == IIO_INTENSITY) {
111062306a36Sopenharmony_ci			switch (dir) {
111162306a36Sopenharmony_ci			case IIO_EV_DIR_RISING:
111262306a36Sopenharmony_ci				chip->settings.als_thresh_high = val;
111362306a36Sopenharmony_ci				ret = 0;
111462306a36Sopenharmony_ci				break;
111562306a36Sopenharmony_ci			case IIO_EV_DIR_FALLING:
111662306a36Sopenharmony_ci				chip->settings.als_thresh_low = val;
111762306a36Sopenharmony_ci				ret = 0;
111862306a36Sopenharmony_ci				break;
111962306a36Sopenharmony_ci			default:
112062306a36Sopenharmony_ci				break;
112162306a36Sopenharmony_ci			}
112262306a36Sopenharmony_ci		} else {
112362306a36Sopenharmony_ci			switch (dir) {
112462306a36Sopenharmony_ci			case IIO_EV_DIR_RISING:
112562306a36Sopenharmony_ci				chip->settings.prox_thres_high = val;
112662306a36Sopenharmony_ci				ret = 0;
112762306a36Sopenharmony_ci				break;
112862306a36Sopenharmony_ci			case IIO_EV_DIR_FALLING:
112962306a36Sopenharmony_ci				chip->settings.prox_thres_low = val;
113062306a36Sopenharmony_ci				ret = 0;
113162306a36Sopenharmony_ci				break;
113262306a36Sopenharmony_ci			default:
113362306a36Sopenharmony_ci				break;
113462306a36Sopenharmony_ci			}
113562306a36Sopenharmony_ci		}
113662306a36Sopenharmony_ci		break;
113762306a36Sopenharmony_ci	case IIO_EV_INFO_PERIOD:
113862306a36Sopenharmony_ci		if (chan->type == IIO_INTENSITY)
113962306a36Sopenharmony_ci			time = chip->settings.als_time;
114062306a36Sopenharmony_ci		else
114162306a36Sopenharmony_ci			time = chip->settings.prox_time;
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci		count = 256 - time;
114462306a36Sopenharmony_ci		persistence = ((val * 1000000) + val2) /
114562306a36Sopenharmony_ci			(count * tsl2772_int_time_avail[chip->id][3]);
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci		if (chan->type == IIO_INTENSITY) {
114862306a36Sopenharmony_ci			/* ALS filter values are 1, 2, 3, 5, 10, 15, ..., 60 */
114962306a36Sopenharmony_ci			if (persistence > 3)
115062306a36Sopenharmony_ci				persistence = (persistence / 5) + 3;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci			chip->settings.als_persistence = persistence;
115362306a36Sopenharmony_ci		} else {
115462306a36Sopenharmony_ci			chip->settings.prox_persistence = persistence;
115562306a36Sopenharmony_ci		}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci		ret = 0;
115862306a36Sopenharmony_ci		break;
115962306a36Sopenharmony_ci	default:
116062306a36Sopenharmony_ci		break;
116162306a36Sopenharmony_ci	}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	if (ret < 0)
116462306a36Sopenharmony_ci		return ret;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	return tsl2772_invoke_change(indio_dev);
116762306a36Sopenharmony_ci}
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_cistatic int tsl2772_read_event_value(struct iio_dev *indio_dev,
117062306a36Sopenharmony_ci				    const struct iio_chan_spec *chan,
117162306a36Sopenharmony_ci				    enum iio_event_type type,
117262306a36Sopenharmony_ci				    enum iio_event_direction dir,
117362306a36Sopenharmony_ci				    enum iio_event_info info,
117462306a36Sopenharmony_ci				    int *val, int *val2)
117562306a36Sopenharmony_ci{
117662306a36Sopenharmony_ci	struct tsl2772_chip *chip = iio_priv(indio_dev);
117762306a36Sopenharmony_ci	int filter_delay, persistence;
117862306a36Sopenharmony_ci	u8 time;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	switch (info) {
118162306a36Sopenharmony_ci	case IIO_EV_INFO_VALUE:
118262306a36Sopenharmony_ci		if (chan->type == IIO_INTENSITY) {
118362306a36Sopenharmony_ci			switch (dir) {
118462306a36Sopenharmony_ci			case IIO_EV_DIR_RISING:
118562306a36Sopenharmony_ci				*val = chip->settings.als_thresh_high;
118662306a36Sopenharmony_ci				return IIO_VAL_INT;
118762306a36Sopenharmony_ci			case IIO_EV_DIR_FALLING:
118862306a36Sopenharmony_ci				*val = chip->settings.als_thresh_low;
118962306a36Sopenharmony_ci				return IIO_VAL_INT;
119062306a36Sopenharmony_ci			default:
119162306a36Sopenharmony_ci				return -EINVAL;
119262306a36Sopenharmony_ci			}
119362306a36Sopenharmony_ci		} else {
119462306a36Sopenharmony_ci			switch (dir) {
119562306a36Sopenharmony_ci			case IIO_EV_DIR_RISING:
119662306a36Sopenharmony_ci				*val = chip->settings.prox_thres_high;
119762306a36Sopenharmony_ci				return IIO_VAL_INT;
119862306a36Sopenharmony_ci			case IIO_EV_DIR_FALLING:
119962306a36Sopenharmony_ci				*val = chip->settings.prox_thres_low;
120062306a36Sopenharmony_ci				return IIO_VAL_INT;
120162306a36Sopenharmony_ci			default:
120262306a36Sopenharmony_ci				return -EINVAL;
120362306a36Sopenharmony_ci			}
120462306a36Sopenharmony_ci		}
120562306a36Sopenharmony_ci		break;
120662306a36Sopenharmony_ci	case IIO_EV_INFO_PERIOD:
120762306a36Sopenharmony_ci		if (chan->type == IIO_INTENSITY) {
120862306a36Sopenharmony_ci			time = chip->settings.als_time;
120962306a36Sopenharmony_ci			persistence = chip->settings.als_persistence;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci			/* ALS filter values are 1, 2, 3, 5, 10, 15, ..., 60 */
121262306a36Sopenharmony_ci			if (persistence > 3)
121362306a36Sopenharmony_ci				persistence = (persistence - 3) * 5;
121462306a36Sopenharmony_ci		} else {
121562306a36Sopenharmony_ci			time = chip->settings.prox_time;
121662306a36Sopenharmony_ci			persistence = chip->settings.prox_persistence;
121762306a36Sopenharmony_ci		}
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci		filter_delay = persistence * (256 - time) *
122062306a36Sopenharmony_ci			tsl2772_int_time_avail[chip->id][3];
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci		*val = filter_delay / 1000000;
122362306a36Sopenharmony_ci		*val2 = filter_delay % 1000000;
122462306a36Sopenharmony_ci		return IIO_VAL_INT_PLUS_MICRO;
122562306a36Sopenharmony_ci	default:
122662306a36Sopenharmony_ci		return -EINVAL;
122762306a36Sopenharmony_ci	}
122862306a36Sopenharmony_ci}
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_cistatic int tsl2772_read_raw(struct iio_dev *indio_dev,
123162306a36Sopenharmony_ci			    struct iio_chan_spec const *chan,
123262306a36Sopenharmony_ci			    int *val,
123362306a36Sopenharmony_ci			    int *val2,
123462306a36Sopenharmony_ci			    long mask)
123562306a36Sopenharmony_ci{
123662306a36Sopenharmony_ci	struct tsl2772_chip *chip = iio_priv(indio_dev);
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	switch (mask) {
123962306a36Sopenharmony_ci	case IIO_CHAN_INFO_PROCESSED:
124062306a36Sopenharmony_ci		switch (chan->type) {
124162306a36Sopenharmony_ci		case IIO_LIGHT:
124262306a36Sopenharmony_ci			tsl2772_get_lux(indio_dev);
124362306a36Sopenharmony_ci			*val = chip->als_cur_info.lux;
124462306a36Sopenharmony_ci			return IIO_VAL_INT;
124562306a36Sopenharmony_ci		default:
124662306a36Sopenharmony_ci			return -EINVAL;
124762306a36Sopenharmony_ci		}
124862306a36Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
124962306a36Sopenharmony_ci		switch (chan->type) {
125062306a36Sopenharmony_ci		case IIO_INTENSITY:
125162306a36Sopenharmony_ci			tsl2772_get_lux(indio_dev);
125262306a36Sopenharmony_ci			if (chan->channel == 0)
125362306a36Sopenharmony_ci				*val = chip->als_cur_info.als_ch0;
125462306a36Sopenharmony_ci			else
125562306a36Sopenharmony_ci				*val = chip->als_cur_info.als_ch1;
125662306a36Sopenharmony_ci			return IIO_VAL_INT;
125762306a36Sopenharmony_ci		case IIO_PROXIMITY:
125862306a36Sopenharmony_ci			tsl2772_get_prox(indio_dev);
125962306a36Sopenharmony_ci			*val = chip->prox_data;
126062306a36Sopenharmony_ci			return IIO_VAL_INT;
126162306a36Sopenharmony_ci		default:
126262306a36Sopenharmony_ci			return -EINVAL;
126362306a36Sopenharmony_ci		}
126462306a36Sopenharmony_ci		break;
126562306a36Sopenharmony_ci	case IIO_CHAN_INFO_CALIBSCALE:
126662306a36Sopenharmony_ci		if (chan->type == IIO_LIGHT)
126762306a36Sopenharmony_ci			*val = tsl2772_als_gain[chip->settings.als_gain];
126862306a36Sopenharmony_ci		else
126962306a36Sopenharmony_ci			*val = tsl2772_prox_gain[chip->settings.prox_gain];
127062306a36Sopenharmony_ci		return IIO_VAL_INT;
127162306a36Sopenharmony_ci	case IIO_CHAN_INFO_CALIBBIAS:
127262306a36Sopenharmony_ci		*val = chip->settings.als_gain_trim;
127362306a36Sopenharmony_ci		return IIO_VAL_INT;
127462306a36Sopenharmony_ci	case IIO_CHAN_INFO_INT_TIME:
127562306a36Sopenharmony_ci		*val = 0;
127662306a36Sopenharmony_ci		*val2 = (256 - chip->settings.als_time) *
127762306a36Sopenharmony_ci			tsl2772_int_time_avail[chip->id][3];
127862306a36Sopenharmony_ci		return IIO_VAL_INT_PLUS_MICRO;
127962306a36Sopenharmony_ci	default:
128062306a36Sopenharmony_ci		return -EINVAL;
128162306a36Sopenharmony_ci	}
128262306a36Sopenharmony_ci}
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_cistatic int tsl2772_write_raw(struct iio_dev *indio_dev,
128562306a36Sopenharmony_ci			     struct iio_chan_spec const *chan,
128662306a36Sopenharmony_ci			     int val,
128762306a36Sopenharmony_ci			     int val2,
128862306a36Sopenharmony_ci			     long mask)
128962306a36Sopenharmony_ci{
129062306a36Sopenharmony_ci	struct tsl2772_chip *chip = iio_priv(indio_dev);
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	switch (mask) {
129362306a36Sopenharmony_ci	case IIO_CHAN_INFO_CALIBSCALE:
129462306a36Sopenharmony_ci		if (chan->type == IIO_INTENSITY) {
129562306a36Sopenharmony_ci			switch (val) {
129662306a36Sopenharmony_ci			case 1:
129762306a36Sopenharmony_ci				chip->settings.als_gain = 0;
129862306a36Sopenharmony_ci				break;
129962306a36Sopenharmony_ci			case 8:
130062306a36Sopenharmony_ci				chip->settings.als_gain = 1;
130162306a36Sopenharmony_ci				break;
130262306a36Sopenharmony_ci			case 16:
130362306a36Sopenharmony_ci				chip->settings.als_gain = 2;
130462306a36Sopenharmony_ci				break;
130562306a36Sopenharmony_ci			case 120:
130662306a36Sopenharmony_ci				chip->settings.als_gain = 3;
130762306a36Sopenharmony_ci				break;
130862306a36Sopenharmony_ci			default:
130962306a36Sopenharmony_ci				return -EINVAL;
131062306a36Sopenharmony_ci			}
131162306a36Sopenharmony_ci		} else {
131262306a36Sopenharmony_ci			switch (val) {
131362306a36Sopenharmony_ci			case 1:
131462306a36Sopenharmony_ci				chip->settings.prox_gain = 0;
131562306a36Sopenharmony_ci				break;
131662306a36Sopenharmony_ci			case 2:
131762306a36Sopenharmony_ci				chip->settings.prox_gain = 1;
131862306a36Sopenharmony_ci				break;
131962306a36Sopenharmony_ci			case 4:
132062306a36Sopenharmony_ci				chip->settings.prox_gain = 2;
132162306a36Sopenharmony_ci				break;
132262306a36Sopenharmony_ci			case 8:
132362306a36Sopenharmony_ci				chip->settings.prox_gain = 3;
132462306a36Sopenharmony_ci				break;
132562306a36Sopenharmony_ci			default:
132662306a36Sopenharmony_ci				return -EINVAL;
132762306a36Sopenharmony_ci			}
132862306a36Sopenharmony_ci		}
132962306a36Sopenharmony_ci		break;
133062306a36Sopenharmony_ci	case IIO_CHAN_INFO_CALIBBIAS:
133162306a36Sopenharmony_ci		if (val < TSL2772_ALS_GAIN_TRIM_MIN ||
133262306a36Sopenharmony_ci		    val > TSL2772_ALS_GAIN_TRIM_MAX)
133362306a36Sopenharmony_ci			return -EINVAL;
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci		chip->settings.als_gain_trim = val;
133662306a36Sopenharmony_ci		break;
133762306a36Sopenharmony_ci	case IIO_CHAN_INFO_INT_TIME:
133862306a36Sopenharmony_ci		if (val != 0 || val2 < tsl2772_int_time_avail[chip->id][1] ||
133962306a36Sopenharmony_ci		    val2 > tsl2772_int_time_avail[chip->id][5])
134062306a36Sopenharmony_ci			return -EINVAL;
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci		chip->settings.als_time = 256 -
134362306a36Sopenharmony_ci			(val2 / tsl2772_int_time_avail[chip->id][3]);
134462306a36Sopenharmony_ci		break;
134562306a36Sopenharmony_ci	default:
134662306a36Sopenharmony_ci		return -EINVAL;
134762306a36Sopenharmony_ci	}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	return tsl2772_invoke_change(indio_dev);
135062306a36Sopenharmony_ci}
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_cistatic DEVICE_ATTR_RW(in_illuminance0_target_input);
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_cistatic DEVICE_ATTR_WO(in_illuminance0_calibrate);
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_cistatic DEVICE_ATTR_WO(in_proximity0_calibrate);
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(in_illuminance0_lux_table);
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci/* Use the default register values to identify the Taos device */
136162306a36Sopenharmony_cistatic int tsl2772_device_id_verif(int id, int target)
136262306a36Sopenharmony_ci{
136362306a36Sopenharmony_ci	switch (target) {
136462306a36Sopenharmony_ci	case tsl2571:
136562306a36Sopenharmony_ci	case tsl2671:
136662306a36Sopenharmony_ci	case tsl2771:
136762306a36Sopenharmony_ci		return (id & 0xf0) == TRITON_ID;
136862306a36Sopenharmony_ci	case tmd2671:
136962306a36Sopenharmony_ci	case tmd2771:
137062306a36Sopenharmony_ci		return (id & 0xf0) == HALIBUT_ID;
137162306a36Sopenharmony_ci	case tsl2572:
137262306a36Sopenharmony_ci	case tsl2672:
137362306a36Sopenharmony_ci	case tmd2672:
137462306a36Sopenharmony_ci	case tsl2772:
137562306a36Sopenharmony_ci	case tmd2772:
137662306a36Sopenharmony_ci	case apds9930:
137762306a36Sopenharmony_ci		return (id & 0xf0) == SWORDFISH_ID;
137862306a36Sopenharmony_ci	}
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	return -EINVAL;
138162306a36Sopenharmony_ci}
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_cistatic irqreturn_t tsl2772_event_handler(int irq, void *private)
138462306a36Sopenharmony_ci{
138562306a36Sopenharmony_ci	struct iio_dev *indio_dev = private;
138662306a36Sopenharmony_ci	struct tsl2772_chip *chip = iio_priv(indio_dev);
138762306a36Sopenharmony_ci	s64 timestamp = iio_get_time_ns(indio_dev);
138862306a36Sopenharmony_ci	int ret;
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	ret = tsl2772_read_status(chip);
139162306a36Sopenharmony_ci	if (ret < 0)
139262306a36Sopenharmony_ci		return IRQ_HANDLED;
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	/* What type of interrupt do we need to process */
139562306a36Sopenharmony_ci	if (ret & TSL2772_STA_PRX_INTR) {
139662306a36Sopenharmony_ci		iio_push_event(indio_dev,
139762306a36Sopenharmony_ci			       IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY,
139862306a36Sopenharmony_ci						    0,
139962306a36Sopenharmony_ci						    IIO_EV_TYPE_THRESH,
140062306a36Sopenharmony_ci						    IIO_EV_DIR_EITHER),
140162306a36Sopenharmony_ci			       timestamp);
140262306a36Sopenharmony_ci	}
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	if (ret & TSL2772_STA_ALS_INTR) {
140562306a36Sopenharmony_ci		iio_push_event(indio_dev,
140662306a36Sopenharmony_ci			       IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
140762306a36Sopenharmony_ci						    0,
140862306a36Sopenharmony_ci						    IIO_EV_TYPE_THRESH,
140962306a36Sopenharmony_ci						    IIO_EV_DIR_EITHER),
141062306a36Sopenharmony_ci			       timestamp);
141162306a36Sopenharmony_ci	}
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	ret = i2c_smbus_write_byte(chip->client,
141462306a36Sopenharmony_ci				   TSL2772_CMD_REG | TSL2772_CMD_SPL_FN |
141562306a36Sopenharmony_ci				   TSL2772_CMD_PROXALS_INT_CLR);
141662306a36Sopenharmony_ci	if (ret < 0)
141762306a36Sopenharmony_ci		dev_err(&chip->client->dev,
141862306a36Sopenharmony_ci			"%s: failed to clear interrupt status: %d\n",
141962306a36Sopenharmony_ci			__func__, ret);
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	return IRQ_HANDLED;
142262306a36Sopenharmony_ci}
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_cistatic struct attribute *tsl2772_ALS_device_attrs[] = {
142562306a36Sopenharmony_ci	&dev_attr_in_illuminance0_target_input.attr,
142662306a36Sopenharmony_ci	&dev_attr_in_illuminance0_calibrate.attr,
142762306a36Sopenharmony_ci	&dev_attr_in_illuminance0_lux_table.attr,
142862306a36Sopenharmony_ci	NULL
142962306a36Sopenharmony_ci};
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_cistatic struct attribute *tsl2772_PRX_device_attrs[] = {
143262306a36Sopenharmony_ci	&dev_attr_in_proximity0_calibrate.attr,
143362306a36Sopenharmony_ci	NULL
143462306a36Sopenharmony_ci};
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_cistatic struct attribute *tsl2772_ALSPRX_device_attrs[] = {
143762306a36Sopenharmony_ci	&dev_attr_in_illuminance0_target_input.attr,
143862306a36Sopenharmony_ci	&dev_attr_in_illuminance0_calibrate.attr,
143962306a36Sopenharmony_ci	&dev_attr_in_illuminance0_lux_table.attr,
144062306a36Sopenharmony_ci	NULL
144162306a36Sopenharmony_ci};
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_cistatic struct attribute *tsl2772_PRX2_device_attrs[] = {
144462306a36Sopenharmony_ci	&dev_attr_in_proximity0_calibrate.attr,
144562306a36Sopenharmony_ci	NULL
144662306a36Sopenharmony_ci};
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_cistatic struct attribute *tsl2772_ALSPRX2_device_attrs[] = {
144962306a36Sopenharmony_ci	&dev_attr_in_illuminance0_target_input.attr,
145062306a36Sopenharmony_ci	&dev_attr_in_illuminance0_calibrate.attr,
145162306a36Sopenharmony_ci	&dev_attr_in_illuminance0_lux_table.attr,
145262306a36Sopenharmony_ci	&dev_attr_in_proximity0_calibrate.attr,
145362306a36Sopenharmony_ci	NULL
145462306a36Sopenharmony_ci};
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_cistatic const struct attribute_group tsl2772_device_attr_group_tbl[] = {
145762306a36Sopenharmony_ci	[ALS] = {
145862306a36Sopenharmony_ci		.attrs = tsl2772_ALS_device_attrs,
145962306a36Sopenharmony_ci	},
146062306a36Sopenharmony_ci	[PRX] = {
146162306a36Sopenharmony_ci		.attrs = tsl2772_PRX_device_attrs,
146262306a36Sopenharmony_ci	},
146362306a36Sopenharmony_ci	[ALSPRX] = {
146462306a36Sopenharmony_ci		.attrs = tsl2772_ALSPRX_device_attrs,
146562306a36Sopenharmony_ci	},
146662306a36Sopenharmony_ci	[PRX2] = {
146762306a36Sopenharmony_ci		.attrs = tsl2772_PRX2_device_attrs,
146862306a36Sopenharmony_ci	},
146962306a36Sopenharmony_ci	[ALSPRX2] = {
147062306a36Sopenharmony_ci		.attrs = tsl2772_ALSPRX2_device_attrs,
147162306a36Sopenharmony_ci	},
147262306a36Sopenharmony_ci};
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci#define TSL2772_DEVICE_INFO(type)[type] = \
147562306a36Sopenharmony_ci	{ \
147662306a36Sopenharmony_ci		.attrs = &tsl2772_device_attr_group_tbl[type], \
147762306a36Sopenharmony_ci		.read_raw = &tsl2772_read_raw, \
147862306a36Sopenharmony_ci		.read_avail = &tsl2772_read_avail, \
147962306a36Sopenharmony_ci		.write_raw = &tsl2772_write_raw, \
148062306a36Sopenharmony_ci		.read_event_value = &tsl2772_read_event_value, \
148162306a36Sopenharmony_ci		.write_event_value = &tsl2772_write_event_value, \
148262306a36Sopenharmony_ci		.read_event_config = &tsl2772_read_interrupt_config, \
148362306a36Sopenharmony_ci		.write_event_config = &tsl2772_write_interrupt_config, \
148462306a36Sopenharmony_ci	}
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_cistatic const struct iio_info tsl2772_device_info[] = {
148762306a36Sopenharmony_ci	TSL2772_DEVICE_INFO(ALS),
148862306a36Sopenharmony_ci	TSL2772_DEVICE_INFO(PRX),
148962306a36Sopenharmony_ci	TSL2772_DEVICE_INFO(ALSPRX),
149062306a36Sopenharmony_ci	TSL2772_DEVICE_INFO(PRX2),
149162306a36Sopenharmony_ci	TSL2772_DEVICE_INFO(ALSPRX2),
149262306a36Sopenharmony_ci};
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_cistatic const struct iio_event_spec tsl2772_events[] = {
149562306a36Sopenharmony_ci	{
149662306a36Sopenharmony_ci		.type = IIO_EV_TYPE_THRESH,
149762306a36Sopenharmony_ci		.dir = IIO_EV_DIR_RISING,
149862306a36Sopenharmony_ci		.mask_separate = BIT(IIO_EV_INFO_VALUE),
149962306a36Sopenharmony_ci	}, {
150062306a36Sopenharmony_ci		.type = IIO_EV_TYPE_THRESH,
150162306a36Sopenharmony_ci		.dir = IIO_EV_DIR_FALLING,
150262306a36Sopenharmony_ci		.mask_separate = BIT(IIO_EV_INFO_VALUE),
150362306a36Sopenharmony_ci	}, {
150462306a36Sopenharmony_ci		.type = IIO_EV_TYPE_THRESH,
150562306a36Sopenharmony_ci		.dir = IIO_EV_DIR_EITHER,
150662306a36Sopenharmony_ci		.mask_separate = BIT(IIO_EV_INFO_PERIOD) |
150762306a36Sopenharmony_ci			BIT(IIO_EV_INFO_ENABLE),
150862306a36Sopenharmony_ci	},
150962306a36Sopenharmony_ci};
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_cistatic const struct tsl2772_chip_info tsl2772_chip_info_tbl[] = {
151262306a36Sopenharmony_ci	[ALS] = {
151362306a36Sopenharmony_ci		.channel_with_events = {
151462306a36Sopenharmony_ci			{
151562306a36Sopenharmony_ci			.type = IIO_LIGHT,
151662306a36Sopenharmony_ci			.indexed = 1,
151762306a36Sopenharmony_ci			.channel = 0,
151862306a36Sopenharmony_ci			.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
151962306a36Sopenharmony_ci			}, {
152062306a36Sopenharmony_ci			.type = IIO_INTENSITY,
152162306a36Sopenharmony_ci			.indexed = 1,
152262306a36Sopenharmony_ci			.channel = 0,
152362306a36Sopenharmony_ci			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
152462306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_INT_TIME) |
152562306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBSCALE) |
152662306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBBIAS),
152762306a36Sopenharmony_ci			.info_mask_separate_available =
152862306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_INT_TIME) |
152962306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBSCALE),
153062306a36Sopenharmony_ci			.event_spec = tsl2772_events,
153162306a36Sopenharmony_ci			.num_event_specs = ARRAY_SIZE(tsl2772_events),
153262306a36Sopenharmony_ci			}, {
153362306a36Sopenharmony_ci			.type = IIO_INTENSITY,
153462306a36Sopenharmony_ci			.indexed = 1,
153562306a36Sopenharmony_ci			.channel = 1,
153662306a36Sopenharmony_ci			},
153762306a36Sopenharmony_ci		},
153862306a36Sopenharmony_ci		.channel_without_events = {
153962306a36Sopenharmony_ci			{
154062306a36Sopenharmony_ci			.type = IIO_LIGHT,
154162306a36Sopenharmony_ci			.indexed = 1,
154262306a36Sopenharmony_ci			.channel = 0,
154362306a36Sopenharmony_ci			.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
154462306a36Sopenharmony_ci			}, {
154562306a36Sopenharmony_ci			.type = IIO_INTENSITY,
154662306a36Sopenharmony_ci			.indexed = 1,
154762306a36Sopenharmony_ci			.channel = 0,
154862306a36Sopenharmony_ci			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
154962306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_INT_TIME) |
155062306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBSCALE) |
155162306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBBIAS),
155262306a36Sopenharmony_ci			.info_mask_separate_available =
155362306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_INT_TIME) |
155462306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBSCALE),
155562306a36Sopenharmony_ci			}, {
155662306a36Sopenharmony_ci			.type = IIO_INTENSITY,
155762306a36Sopenharmony_ci			.indexed = 1,
155862306a36Sopenharmony_ci			.channel = 1,
155962306a36Sopenharmony_ci			},
156062306a36Sopenharmony_ci		},
156162306a36Sopenharmony_ci		.chan_table_elements = 3,
156262306a36Sopenharmony_ci		.info = &tsl2772_device_info[ALS],
156362306a36Sopenharmony_ci	},
156462306a36Sopenharmony_ci	[PRX] = {
156562306a36Sopenharmony_ci		.channel_with_events = {
156662306a36Sopenharmony_ci			{
156762306a36Sopenharmony_ci			.type = IIO_PROXIMITY,
156862306a36Sopenharmony_ci			.indexed = 1,
156962306a36Sopenharmony_ci			.channel = 0,
157062306a36Sopenharmony_ci			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
157162306a36Sopenharmony_ci			.event_spec = tsl2772_events,
157262306a36Sopenharmony_ci			.num_event_specs = ARRAY_SIZE(tsl2772_events),
157362306a36Sopenharmony_ci			},
157462306a36Sopenharmony_ci		},
157562306a36Sopenharmony_ci		.channel_without_events = {
157662306a36Sopenharmony_ci			{
157762306a36Sopenharmony_ci			.type = IIO_PROXIMITY,
157862306a36Sopenharmony_ci			.indexed = 1,
157962306a36Sopenharmony_ci			.channel = 0,
158062306a36Sopenharmony_ci			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
158162306a36Sopenharmony_ci			},
158262306a36Sopenharmony_ci		},
158362306a36Sopenharmony_ci		.chan_table_elements = 1,
158462306a36Sopenharmony_ci		.info = &tsl2772_device_info[PRX],
158562306a36Sopenharmony_ci	},
158662306a36Sopenharmony_ci	[ALSPRX] = {
158762306a36Sopenharmony_ci		.channel_with_events = {
158862306a36Sopenharmony_ci			{
158962306a36Sopenharmony_ci			.type = IIO_LIGHT,
159062306a36Sopenharmony_ci			.indexed = 1,
159162306a36Sopenharmony_ci			.channel = 0,
159262306a36Sopenharmony_ci			.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
159362306a36Sopenharmony_ci			}, {
159462306a36Sopenharmony_ci			.type = IIO_INTENSITY,
159562306a36Sopenharmony_ci			.indexed = 1,
159662306a36Sopenharmony_ci			.channel = 0,
159762306a36Sopenharmony_ci			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
159862306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_INT_TIME) |
159962306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBSCALE) |
160062306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBBIAS),
160162306a36Sopenharmony_ci			.info_mask_separate_available =
160262306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_INT_TIME) |
160362306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBSCALE),
160462306a36Sopenharmony_ci			.event_spec = tsl2772_events,
160562306a36Sopenharmony_ci			.num_event_specs = ARRAY_SIZE(tsl2772_events),
160662306a36Sopenharmony_ci			}, {
160762306a36Sopenharmony_ci			.type = IIO_INTENSITY,
160862306a36Sopenharmony_ci			.indexed = 1,
160962306a36Sopenharmony_ci			.channel = 1,
161062306a36Sopenharmony_ci			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
161162306a36Sopenharmony_ci			}, {
161262306a36Sopenharmony_ci			.type = IIO_PROXIMITY,
161362306a36Sopenharmony_ci			.indexed = 1,
161462306a36Sopenharmony_ci			.channel = 0,
161562306a36Sopenharmony_ci			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
161662306a36Sopenharmony_ci			.event_spec = tsl2772_events,
161762306a36Sopenharmony_ci			.num_event_specs = ARRAY_SIZE(tsl2772_events),
161862306a36Sopenharmony_ci			},
161962306a36Sopenharmony_ci		},
162062306a36Sopenharmony_ci		.channel_without_events = {
162162306a36Sopenharmony_ci			{
162262306a36Sopenharmony_ci			.type = IIO_LIGHT,
162362306a36Sopenharmony_ci			.indexed = 1,
162462306a36Sopenharmony_ci			.channel = 0,
162562306a36Sopenharmony_ci			.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
162662306a36Sopenharmony_ci			}, {
162762306a36Sopenharmony_ci			.type = IIO_INTENSITY,
162862306a36Sopenharmony_ci			.indexed = 1,
162962306a36Sopenharmony_ci			.channel = 0,
163062306a36Sopenharmony_ci			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
163162306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_INT_TIME) |
163262306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBSCALE) |
163362306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBBIAS),
163462306a36Sopenharmony_ci			.info_mask_separate_available =
163562306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_INT_TIME) |
163662306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBSCALE),
163762306a36Sopenharmony_ci			}, {
163862306a36Sopenharmony_ci			.type = IIO_INTENSITY,
163962306a36Sopenharmony_ci			.indexed = 1,
164062306a36Sopenharmony_ci			.channel = 1,
164162306a36Sopenharmony_ci			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
164262306a36Sopenharmony_ci			}, {
164362306a36Sopenharmony_ci			.type = IIO_PROXIMITY,
164462306a36Sopenharmony_ci			.indexed = 1,
164562306a36Sopenharmony_ci			.channel = 0,
164662306a36Sopenharmony_ci			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
164762306a36Sopenharmony_ci			},
164862306a36Sopenharmony_ci		},
164962306a36Sopenharmony_ci		.chan_table_elements = 4,
165062306a36Sopenharmony_ci		.info = &tsl2772_device_info[ALSPRX],
165162306a36Sopenharmony_ci	},
165262306a36Sopenharmony_ci	[PRX2] = {
165362306a36Sopenharmony_ci		.channel_with_events = {
165462306a36Sopenharmony_ci			{
165562306a36Sopenharmony_ci			.type = IIO_PROXIMITY,
165662306a36Sopenharmony_ci			.indexed = 1,
165762306a36Sopenharmony_ci			.channel = 0,
165862306a36Sopenharmony_ci			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
165962306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBSCALE),
166062306a36Sopenharmony_ci			.info_mask_separate_available =
166162306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBSCALE),
166262306a36Sopenharmony_ci			.event_spec = tsl2772_events,
166362306a36Sopenharmony_ci			.num_event_specs = ARRAY_SIZE(tsl2772_events),
166462306a36Sopenharmony_ci			},
166562306a36Sopenharmony_ci		},
166662306a36Sopenharmony_ci		.channel_without_events = {
166762306a36Sopenharmony_ci			{
166862306a36Sopenharmony_ci			.type = IIO_PROXIMITY,
166962306a36Sopenharmony_ci			.indexed = 1,
167062306a36Sopenharmony_ci			.channel = 0,
167162306a36Sopenharmony_ci			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
167262306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBSCALE),
167362306a36Sopenharmony_ci			.info_mask_separate_available =
167462306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBSCALE),
167562306a36Sopenharmony_ci			},
167662306a36Sopenharmony_ci		},
167762306a36Sopenharmony_ci		.chan_table_elements = 1,
167862306a36Sopenharmony_ci		.info = &tsl2772_device_info[PRX2],
167962306a36Sopenharmony_ci	},
168062306a36Sopenharmony_ci	[ALSPRX2] = {
168162306a36Sopenharmony_ci		.channel_with_events = {
168262306a36Sopenharmony_ci			{
168362306a36Sopenharmony_ci			.type = IIO_LIGHT,
168462306a36Sopenharmony_ci			.indexed = 1,
168562306a36Sopenharmony_ci			.channel = 0,
168662306a36Sopenharmony_ci			.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
168762306a36Sopenharmony_ci			}, {
168862306a36Sopenharmony_ci			.type = IIO_INTENSITY,
168962306a36Sopenharmony_ci			.indexed = 1,
169062306a36Sopenharmony_ci			.channel = 0,
169162306a36Sopenharmony_ci			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
169262306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_INT_TIME) |
169362306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBSCALE) |
169462306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBBIAS),
169562306a36Sopenharmony_ci			.info_mask_separate_available =
169662306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_INT_TIME) |
169762306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBSCALE),
169862306a36Sopenharmony_ci			.event_spec = tsl2772_events,
169962306a36Sopenharmony_ci			.num_event_specs = ARRAY_SIZE(tsl2772_events),
170062306a36Sopenharmony_ci			}, {
170162306a36Sopenharmony_ci			.type = IIO_INTENSITY,
170262306a36Sopenharmony_ci			.indexed = 1,
170362306a36Sopenharmony_ci			.channel = 1,
170462306a36Sopenharmony_ci			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
170562306a36Sopenharmony_ci			}, {
170662306a36Sopenharmony_ci			.type = IIO_PROXIMITY,
170762306a36Sopenharmony_ci			.indexed = 1,
170862306a36Sopenharmony_ci			.channel = 0,
170962306a36Sopenharmony_ci			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
171062306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBSCALE),
171162306a36Sopenharmony_ci			.info_mask_separate_available =
171262306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBSCALE),
171362306a36Sopenharmony_ci			.event_spec = tsl2772_events,
171462306a36Sopenharmony_ci			.num_event_specs = ARRAY_SIZE(tsl2772_events),
171562306a36Sopenharmony_ci			},
171662306a36Sopenharmony_ci		},
171762306a36Sopenharmony_ci		.channel_without_events = {
171862306a36Sopenharmony_ci			{
171962306a36Sopenharmony_ci			.type = IIO_LIGHT,
172062306a36Sopenharmony_ci			.indexed = 1,
172162306a36Sopenharmony_ci			.channel = 0,
172262306a36Sopenharmony_ci			.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
172362306a36Sopenharmony_ci			}, {
172462306a36Sopenharmony_ci			.type = IIO_INTENSITY,
172562306a36Sopenharmony_ci			.indexed = 1,
172662306a36Sopenharmony_ci			.channel = 0,
172762306a36Sopenharmony_ci			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
172862306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_INT_TIME) |
172962306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBSCALE) |
173062306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBBIAS),
173162306a36Sopenharmony_ci			.info_mask_separate_available =
173262306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_INT_TIME) |
173362306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBSCALE),
173462306a36Sopenharmony_ci			}, {
173562306a36Sopenharmony_ci			.type = IIO_INTENSITY,
173662306a36Sopenharmony_ci			.indexed = 1,
173762306a36Sopenharmony_ci			.channel = 1,
173862306a36Sopenharmony_ci			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
173962306a36Sopenharmony_ci			}, {
174062306a36Sopenharmony_ci			.type = IIO_PROXIMITY,
174162306a36Sopenharmony_ci			.indexed = 1,
174262306a36Sopenharmony_ci			.channel = 0,
174362306a36Sopenharmony_ci			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
174462306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBSCALE),
174562306a36Sopenharmony_ci			.info_mask_separate_available =
174662306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_CALIBSCALE),
174762306a36Sopenharmony_ci			},
174862306a36Sopenharmony_ci		},
174962306a36Sopenharmony_ci		.chan_table_elements = 4,
175062306a36Sopenharmony_ci		.info = &tsl2772_device_info[ALSPRX2],
175162306a36Sopenharmony_ci	},
175262306a36Sopenharmony_ci};
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_cistatic int tsl2772_probe(struct i2c_client *clientp)
175562306a36Sopenharmony_ci{
175662306a36Sopenharmony_ci	const struct i2c_device_id *id = i2c_client_get_device_id(clientp);
175762306a36Sopenharmony_ci	struct iio_dev *indio_dev;
175862306a36Sopenharmony_ci	struct tsl2772_chip *chip;
175962306a36Sopenharmony_ci	int ret;
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_ci	indio_dev = devm_iio_device_alloc(&clientp->dev, sizeof(*chip));
176262306a36Sopenharmony_ci	if (!indio_dev)
176362306a36Sopenharmony_ci		return -ENOMEM;
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	chip = iio_priv(indio_dev);
176662306a36Sopenharmony_ci	chip->client = clientp;
176762306a36Sopenharmony_ci	i2c_set_clientdata(clientp, indio_dev);
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_ci	chip->supplies[TSL2772_SUPPLY_VDD].supply = "vdd";
177062306a36Sopenharmony_ci	chip->supplies[TSL2772_SUPPLY_VDDIO].supply = "vddio";
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci	ret = devm_regulator_bulk_get(&clientp->dev,
177362306a36Sopenharmony_ci				      ARRAY_SIZE(chip->supplies),
177462306a36Sopenharmony_ci				      chip->supplies);
177562306a36Sopenharmony_ci	if (ret < 0)
177662306a36Sopenharmony_ci		return dev_err_probe(&clientp->dev, ret, "Failed to get regulators\n");
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_ci	ret = regulator_bulk_enable(ARRAY_SIZE(chip->supplies), chip->supplies);
177962306a36Sopenharmony_ci	if (ret < 0) {
178062306a36Sopenharmony_ci		dev_err(&clientp->dev, "Failed to enable regulators: %d\n",
178162306a36Sopenharmony_ci			ret);
178262306a36Sopenharmony_ci		return ret;
178362306a36Sopenharmony_ci	}
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci	ret = devm_add_action_or_reset(&clientp->dev,
178662306a36Sopenharmony_ci					tsl2772_disable_regulators_action,
178762306a36Sopenharmony_ci					chip);
178862306a36Sopenharmony_ci	if (ret < 0) {
178962306a36Sopenharmony_ci		dev_err(&clientp->dev, "Failed to setup regulator cleanup action %d\n",
179062306a36Sopenharmony_ci			ret);
179162306a36Sopenharmony_ci		return ret;
179262306a36Sopenharmony_ci	}
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci	usleep_range(TSL2772_BOOT_MIN_SLEEP_TIME, TSL2772_BOOT_MAX_SLEEP_TIME);
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci	ret = i2c_smbus_read_byte_data(chip->client,
179762306a36Sopenharmony_ci				       TSL2772_CMD_REG | TSL2772_CHIPID);
179862306a36Sopenharmony_ci	if (ret < 0)
179962306a36Sopenharmony_ci		return ret;
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci	if (tsl2772_device_id_verif(ret, id->driver_data) <= 0) {
180262306a36Sopenharmony_ci		dev_info(&chip->client->dev,
180362306a36Sopenharmony_ci			 "%s: i2c device found does not match expected id\n",
180462306a36Sopenharmony_ci				__func__);
180562306a36Sopenharmony_ci		return -EINVAL;
180662306a36Sopenharmony_ci	}
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	ret = i2c_smbus_write_byte(clientp, TSL2772_CMD_REG | TSL2772_CNTRL);
180962306a36Sopenharmony_ci	if (ret < 0) {
181062306a36Sopenharmony_ci		dev_err(&clientp->dev,
181162306a36Sopenharmony_ci			"%s: Failed to write to CMD register: %d\n",
181262306a36Sopenharmony_ci			__func__, ret);
181362306a36Sopenharmony_ci		return ret;
181462306a36Sopenharmony_ci	}
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	mutex_init(&chip->als_mutex);
181762306a36Sopenharmony_ci	mutex_init(&chip->prox_mutex);
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	chip->tsl2772_chip_status = TSL2772_CHIP_UNKNOWN;
182062306a36Sopenharmony_ci	chip->pdata = dev_get_platdata(&clientp->dev);
182162306a36Sopenharmony_ci	chip->id = id->driver_data;
182262306a36Sopenharmony_ci	chip->chip_info =
182362306a36Sopenharmony_ci		&tsl2772_chip_info_tbl[device_channel_config[id->driver_data]];
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_ci	indio_dev->info = chip->chip_info->info;
182662306a36Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
182762306a36Sopenharmony_ci	indio_dev->name = chip->client->name;
182862306a36Sopenharmony_ci	indio_dev->num_channels = chip->chip_info->chan_table_elements;
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	if (clientp->irq) {
183162306a36Sopenharmony_ci		indio_dev->channels = chip->chip_info->channel_with_events;
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci		ret = devm_request_threaded_irq(&clientp->dev, clientp->irq,
183462306a36Sopenharmony_ci						NULL,
183562306a36Sopenharmony_ci						&tsl2772_event_handler,
183662306a36Sopenharmony_ci						IRQF_TRIGGER_FALLING |
183762306a36Sopenharmony_ci						IRQF_ONESHOT,
183862306a36Sopenharmony_ci						"TSL2772_event",
183962306a36Sopenharmony_ci						indio_dev);
184062306a36Sopenharmony_ci		if (ret) {
184162306a36Sopenharmony_ci			dev_err(&clientp->dev,
184262306a36Sopenharmony_ci				"%s: irq request failed\n", __func__);
184362306a36Sopenharmony_ci			return ret;
184462306a36Sopenharmony_ci		}
184562306a36Sopenharmony_ci	} else {
184662306a36Sopenharmony_ci		indio_dev->channels = chip->chip_info->channel_without_events;
184762306a36Sopenharmony_ci	}
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci	tsl2772_defaults(chip);
185062306a36Sopenharmony_ci	ret = tsl2772_chip_on(indio_dev);
185162306a36Sopenharmony_ci	if (ret < 0)
185262306a36Sopenharmony_ci		return ret;
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_ci	ret = devm_add_action_or_reset(&clientp->dev,
185562306a36Sopenharmony_ci					tsl2772_chip_off_action,
185662306a36Sopenharmony_ci					indio_dev);
185762306a36Sopenharmony_ci	if (ret < 0)
185862306a36Sopenharmony_ci		return ret;
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci	return devm_iio_device_register(&clientp->dev, indio_dev);
186162306a36Sopenharmony_ci}
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_cistatic int tsl2772_suspend(struct device *dev)
186462306a36Sopenharmony_ci{
186562306a36Sopenharmony_ci	struct iio_dev *indio_dev = dev_get_drvdata(dev);
186662306a36Sopenharmony_ci	struct tsl2772_chip *chip = iio_priv(indio_dev);
186762306a36Sopenharmony_ci	int ret;
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci	ret = tsl2772_chip_off(indio_dev);
187062306a36Sopenharmony_ci	regulator_bulk_disable(ARRAY_SIZE(chip->supplies), chip->supplies);
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	return ret;
187362306a36Sopenharmony_ci}
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_cistatic int tsl2772_resume(struct device *dev)
187662306a36Sopenharmony_ci{
187762306a36Sopenharmony_ci	struct iio_dev *indio_dev = dev_get_drvdata(dev);
187862306a36Sopenharmony_ci	struct tsl2772_chip *chip = iio_priv(indio_dev);
187962306a36Sopenharmony_ci	int ret;
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	ret = regulator_bulk_enable(ARRAY_SIZE(chip->supplies), chip->supplies);
188262306a36Sopenharmony_ci	if (ret < 0)
188362306a36Sopenharmony_ci		return ret;
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci	usleep_range(TSL2772_BOOT_MIN_SLEEP_TIME, TSL2772_BOOT_MAX_SLEEP_TIME);
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	return tsl2772_chip_on(indio_dev);
188862306a36Sopenharmony_ci}
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_cistatic const struct i2c_device_id tsl2772_idtable[] = {
189162306a36Sopenharmony_ci	{ "tsl2571", tsl2571 },
189262306a36Sopenharmony_ci	{ "tsl2671", tsl2671 },
189362306a36Sopenharmony_ci	{ "tmd2671", tmd2671 },
189462306a36Sopenharmony_ci	{ "tsl2771", tsl2771 },
189562306a36Sopenharmony_ci	{ "tmd2771", tmd2771 },
189662306a36Sopenharmony_ci	{ "tsl2572", tsl2572 },
189762306a36Sopenharmony_ci	{ "tsl2672", tsl2672 },
189862306a36Sopenharmony_ci	{ "tmd2672", tmd2672 },
189962306a36Sopenharmony_ci	{ "tsl2772", tsl2772 },
190062306a36Sopenharmony_ci	{ "tmd2772", tmd2772 },
190162306a36Sopenharmony_ci	{ "apds9930", apds9930 },
190262306a36Sopenharmony_ci	{}
190362306a36Sopenharmony_ci};
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tsl2772_idtable);
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_cistatic const struct of_device_id tsl2772_of_match[] = {
190862306a36Sopenharmony_ci	{ .compatible = "amstaos,tsl2571" },
190962306a36Sopenharmony_ci	{ .compatible = "amstaos,tsl2671" },
191062306a36Sopenharmony_ci	{ .compatible = "amstaos,tmd2671" },
191162306a36Sopenharmony_ci	{ .compatible = "amstaos,tsl2771" },
191262306a36Sopenharmony_ci	{ .compatible = "amstaos,tmd2771" },
191362306a36Sopenharmony_ci	{ .compatible = "amstaos,tsl2572" },
191462306a36Sopenharmony_ci	{ .compatible = "amstaos,tsl2672" },
191562306a36Sopenharmony_ci	{ .compatible = "amstaos,tmd2672" },
191662306a36Sopenharmony_ci	{ .compatible = "amstaos,tsl2772" },
191762306a36Sopenharmony_ci	{ .compatible = "amstaos,tmd2772" },
191862306a36Sopenharmony_ci	{ .compatible = "avago,apds9930" },
191962306a36Sopenharmony_ci	{}
192062306a36Sopenharmony_ci};
192162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tsl2772_of_match);
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_cistatic const struct dev_pm_ops tsl2772_pm_ops = {
192462306a36Sopenharmony_ci	.suspend = tsl2772_suspend,
192562306a36Sopenharmony_ci	.resume  = tsl2772_resume,
192662306a36Sopenharmony_ci};
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_cistatic struct i2c_driver tsl2772_driver = {
192962306a36Sopenharmony_ci	.driver = {
193062306a36Sopenharmony_ci		.name = "tsl2772",
193162306a36Sopenharmony_ci		.of_match_table = tsl2772_of_match,
193262306a36Sopenharmony_ci		.pm = &tsl2772_pm_ops,
193362306a36Sopenharmony_ci	},
193462306a36Sopenharmony_ci	.id_table = tsl2772_idtable,
193562306a36Sopenharmony_ci	.probe = tsl2772_probe,
193662306a36Sopenharmony_ci};
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_cimodule_i2c_driver(tsl2772_driver);
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ciMODULE_AUTHOR("J. August Brenner <Jon.Brenner@ams.com>");
194162306a36Sopenharmony_ciMODULE_AUTHOR("Brian Masney <masneyb@onstation.org>");
194262306a36Sopenharmony_ciMODULE_DESCRIPTION("TAOS tsl2772 ambient and proximity light sensor driver");
194362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1944