18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * apds9960.c - Support for Avago APDS9960 gesture/RGB/ALS/proximity sensor
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2015, 2018
68c2ecf20Sopenharmony_ci * Author: Matt Ranostay <matt.ranostay@konsulko.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * TODO: gesture + proximity calib offsets
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
148c2ecf20Sopenharmony_ci#include <linux/delay.h>
158c2ecf20Sopenharmony_ci#include <linux/mutex.h>
168c2ecf20Sopenharmony_ci#include <linux/err.h>
178c2ecf20Sopenharmony_ci#include <linux/irq.h>
188c2ecf20Sopenharmony_ci#include <linux/i2c.h>
198c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
208c2ecf20Sopenharmony_ci#include <linux/regmap.h>
218c2ecf20Sopenharmony_ci#include <linux/iio/iio.h>
228c2ecf20Sopenharmony_ci#include <linux/iio/buffer.h>
238c2ecf20Sopenharmony_ci#include <linux/iio/events.h>
248c2ecf20Sopenharmony_ci#include <linux/iio/kfifo_buf.h>
258c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define APDS9960_REGMAP_NAME	"apds9960_regmap"
288c2ecf20Sopenharmony_ci#define APDS9960_DRV_NAME	"apds9960"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define APDS9960_REG_RAM_START	0x00
318c2ecf20Sopenharmony_ci#define APDS9960_REG_RAM_END	0x7f
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define APDS9960_REG_ENABLE	0x80
348c2ecf20Sopenharmony_ci#define APDS9960_REG_ATIME	0x81
358c2ecf20Sopenharmony_ci#define APDS9960_REG_WTIME	0x83
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define APDS9960_REG_AILTL	0x84
388c2ecf20Sopenharmony_ci#define APDS9960_REG_AILTH	0x85
398c2ecf20Sopenharmony_ci#define APDS9960_REG_AIHTL	0x86
408c2ecf20Sopenharmony_ci#define APDS9960_REG_AIHTH	0x87
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define APDS9960_REG_PILT	0x89
438c2ecf20Sopenharmony_ci#define APDS9960_REG_PIHT	0x8b
448c2ecf20Sopenharmony_ci#define APDS9960_REG_PERS	0x8c
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define APDS9960_REG_CONFIG_1	0x8d
478c2ecf20Sopenharmony_ci#define APDS9960_REG_PPULSE	0x8e
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define APDS9960_REG_CONTROL	0x8f
508c2ecf20Sopenharmony_ci#define APDS9960_REG_CONTROL_AGAIN_MASK		0x03
518c2ecf20Sopenharmony_ci#define APDS9960_REG_CONTROL_PGAIN_MASK		0x0c
528c2ecf20Sopenharmony_ci#define APDS9960_REG_CONTROL_AGAIN_MASK_SHIFT	0
538c2ecf20Sopenharmony_ci#define APDS9960_REG_CONTROL_PGAIN_MASK_SHIFT	2
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#define APDS9960_REG_CONFIG_2	0x90
568c2ecf20Sopenharmony_ci#define APDS9960_REG_ID		0x92
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci#define APDS9960_REG_STATUS	0x93
598c2ecf20Sopenharmony_ci#define APDS9960_REG_STATUS_PS_INT	BIT(5)
608c2ecf20Sopenharmony_ci#define APDS9960_REG_STATUS_ALS_INT	BIT(4)
618c2ecf20Sopenharmony_ci#define APDS9960_REG_STATUS_GINT	BIT(2)
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci#define APDS9960_REG_PDATA	0x9c
648c2ecf20Sopenharmony_ci#define APDS9960_REG_POFFSET_UR	0x9d
658c2ecf20Sopenharmony_ci#define APDS9960_REG_POFFSET_DL 0x9e
668c2ecf20Sopenharmony_ci#define APDS9960_REG_CONFIG_3	0x9f
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci#define APDS9960_REG_GPENTH	0xa0
698c2ecf20Sopenharmony_ci#define APDS9960_REG_GEXTH	0xa1
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci#define APDS9960_REG_GCONF_1	0xa2
728c2ecf20Sopenharmony_ci#define APDS9960_REG_GCONF_1_GFIFO_THRES_MASK		0xc0
738c2ecf20Sopenharmony_ci#define APDS9960_REG_GCONF_1_GFIFO_THRES_MASK_SHIFT	6
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#define APDS9960_REG_GCONF_2	0xa3
768c2ecf20Sopenharmony_ci#define APDS9960_REG_GCONF_2_GGAIN_MASK			0x60
778c2ecf20Sopenharmony_ci#define APDS9960_REG_GCONF_2_GGAIN_MASK_SHIFT		5
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci#define APDS9960_REG_GOFFSET_U	0xa4
808c2ecf20Sopenharmony_ci#define APDS9960_REG_GOFFSET_D	0xa5
818c2ecf20Sopenharmony_ci#define APDS9960_REG_GPULSE	0xa6
828c2ecf20Sopenharmony_ci#define APDS9960_REG_GOFFSET_L	0xa7
838c2ecf20Sopenharmony_ci#define APDS9960_REG_GOFFSET_R	0xa9
848c2ecf20Sopenharmony_ci#define APDS9960_REG_GCONF_3	0xaa
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci#define APDS9960_REG_GCONF_4	0xab
878c2ecf20Sopenharmony_ci#define APDS9960_REG_GFLVL	0xae
888c2ecf20Sopenharmony_ci#define APDS9960_REG_GSTATUS	0xaf
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci#define APDS9960_REG_IFORCE	0xe4
918c2ecf20Sopenharmony_ci#define APDS9960_REG_PICLEAR	0xe5
928c2ecf20Sopenharmony_ci#define APDS9960_REG_CICLEAR	0xe6
938c2ecf20Sopenharmony_ci#define APDS9960_REG_AICLEAR	0xe7
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci#define APDS9960_DEFAULT_PERS	0x33
968c2ecf20Sopenharmony_ci#define APDS9960_DEFAULT_GPENTH	0x50
978c2ecf20Sopenharmony_ci#define APDS9960_DEFAULT_GEXTH	0x40
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci#define APDS9960_MAX_PXS_THRES_VAL	255
1008c2ecf20Sopenharmony_ci#define APDS9960_MAX_ALS_THRES_VAL	0xffff
1018c2ecf20Sopenharmony_ci#define APDS9960_MAX_INT_TIME_IN_US	1000000
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cienum apds9960_als_channel_idx {
1048c2ecf20Sopenharmony_ci	IDX_ALS_CLEAR, IDX_ALS_RED, IDX_ALS_GREEN, IDX_ALS_BLUE,
1058c2ecf20Sopenharmony_ci};
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci#define APDS9960_REG_ALS_BASE	0x94
1088c2ecf20Sopenharmony_ci#define APDS9960_REG_ALS_CHANNEL(_colour) \
1098c2ecf20Sopenharmony_ci	(APDS9960_REG_ALS_BASE + (IDX_ALS_##_colour * 2))
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cienum apds9960_gesture_channel_idx {
1128c2ecf20Sopenharmony_ci	IDX_DIR_UP, IDX_DIR_DOWN, IDX_DIR_LEFT, IDX_DIR_RIGHT,
1138c2ecf20Sopenharmony_ci};
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci#define APDS9960_REG_GFIFO_BASE	0xfc
1168c2ecf20Sopenharmony_ci#define APDS9960_REG_GFIFO_DIR(_dir) \
1178c2ecf20Sopenharmony_ci	(APDS9960_REG_GFIFO_BASE + IDX_DIR_##_dir)
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistruct apds9960_data {
1208c2ecf20Sopenharmony_ci	struct i2c_client *client;
1218c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev;
1228c2ecf20Sopenharmony_ci	struct mutex lock;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	/* regmap fields */
1258c2ecf20Sopenharmony_ci	struct regmap *regmap;
1268c2ecf20Sopenharmony_ci	struct regmap_field *reg_int_als;
1278c2ecf20Sopenharmony_ci	struct regmap_field *reg_int_ges;
1288c2ecf20Sopenharmony_ci	struct regmap_field *reg_int_pxs;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	struct regmap_field *reg_enable_als;
1318c2ecf20Sopenharmony_ci	struct regmap_field *reg_enable_ges;
1328c2ecf20Sopenharmony_ci	struct regmap_field *reg_enable_pxs;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	/* state */
1358c2ecf20Sopenharmony_ci	int als_int;
1368c2ecf20Sopenharmony_ci	int pxs_int;
1378c2ecf20Sopenharmony_ci	int gesture_mode_running;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/* gain values */
1408c2ecf20Sopenharmony_ci	int als_gain;
1418c2ecf20Sopenharmony_ci	int pxs_gain;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	/* integration time value in us */
1448c2ecf20Sopenharmony_ci	int als_adc_int_us;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	/* gesture buffer */
1478c2ecf20Sopenharmony_ci	u8 buffer[4]; /* 4 8-bit channels */
1488c2ecf20Sopenharmony_ci};
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic const struct reg_default apds9960_reg_defaults[] = {
1518c2ecf20Sopenharmony_ci	/* Default ALS integration time = 2.48ms */
1528c2ecf20Sopenharmony_ci	{ APDS9960_REG_ATIME, 0xff },
1538c2ecf20Sopenharmony_ci};
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic const struct regmap_range apds9960_volatile_ranges[] = {
1568c2ecf20Sopenharmony_ci	regmap_reg_range(APDS9960_REG_STATUS,
1578c2ecf20Sopenharmony_ci				APDS9960_REG_PDATA),
1588c2ecf20Sopenharmony_ci	regmap_reg_range(APDS9960_REG_GFLVL,
1598c2ecf20Sopenharmony_ci				APDS9960_REG_GSTATUS),
1608c2ecf20Sopenharmony_ci	regmap_reg_range(APDS9960_REG_GFIFO_DIR(UP),
1618c2ecf20Sopenharmony_ci				APDS9960_REG_GFIFO_DIR(RIGHT)),
1628c2ecf20Sopenharmony_ci	regmap_reg_range(APDS9960_REG_IFORCE,
1638c2ecf20Sopenharmony_ci				APDS9960_REG_AICLEAR),
1648c2ecf20Sopenharmony_ci};
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic const struct regmap_access_table apds9960_volatile_table = {
1678c2ecf20Sopenharmony_ci	.yes_ranges	= apds9960_volatile_ranges,
1688c2ecf20Sopenharmony_ci	.n_yes_ranges	= ARRAY_SIZE(apds9960_volatile_ranges),
1698c2ecf20Sopenharmony_ci};
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic const struct regmap_range apds9960_precious_ranges[] = {
1728c2ecf20Sopenharmony_ci	regmap_reg_range(APDS9960_REG_RAM_START, APDS9960_REG_RAM_END),
1738c2ecf20Sopenharmony_ci};
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic const struct regmap_access_table apds9960_precious_table = {
1768c2ecf20Sopenharmony_ci	.yes_ranges	= apds9960_precious_ranges,
1778c2ecf20Sopenharmony_ci	.n_yes_ranges	= ARRAY_SIZE(apds9960_precious_ranges),
1788c2ecf20Sopenharmony_ci};
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic const struct regmap_range apds9960_readable_ranges[] = {
1818c2ecf20Sopenharmony_ci	regmap_reg_range(APDS9960_REG_ENABLE,
1828c2ecf20Sopenharmony_ci				APDS9960_REG_GSTATUS),
1838c2ecf20Sopenharmony_ci	regmap_reg_range(APDS9960_REG_GFIFO_DIR(UP),
1848c2ecf20Sopenharmony_ci				APDS9960_REG_GFIFO_DIR(RIGHT)),
1858c2ecf20Sopenharmony_ci};
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic const struct regmap_access_table apds9960_readable_table = {
1888c2ecf20Sopenharmony_ci	.yes_ranges	= apds9960_readable_ranges,
1898c2ecf20Sopenharmony_ci	.n_yes_ranges	= ARRAY_SIZE(apds9960_readable_ranges),
1908c2ecf20Sopenharmony_ci};
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic const struct regmap_range apds9960_writeable_ranges[] = {
1938c2ecf20Sopenharmony_ci	regmap_reg_range(APDS9960_REG_ENABLE, APDS9960_REG_CONFIG_2),
1948c2ecf20Sopenharmony_ci	regmap_reg_range(APDS9960_REG_POFFSET_UR, APDS9960_REG_GCONF_4),
1958c2ecf20Sopenharmony_ci	regmap_reg_range(APDS9960_REG_IFORCE, APDS9960_REG_AICLEAR),
1968c2ecf20Sopenharmony_ci};
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic const struct regmap_access_table apds9960_writeable_table = {
1998c2ecf20Sopenharmony_ci	.yes_ranges	= apds9960_writeable_ranges,
2008c2ecf20Sopenharmony_ci	.n_yes_ranges	= ARRAY_SIZE(apds9960_writeable_ranges),
2018c2ecf20Sopenharmony_ci};
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic const struct regmap_config apds9960_regmap_config = {
2048c2ecf20Sopenharmony_ci	.name = APDS9960_REGMAP_NAME,
2058c2ecf20Sopenharmony_ci	.reg_bits = 8,
2068c2ecf20Sopenharmony_ci	.val_bits = 8,
2078c2ecf20Sopenharmony_ci	.use_single_read = true,
2088c2ecf20Sopenharmony_ci	.use_single_write = true,
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	.volatile_table = &apds9960_volatile_table,
2118c2ecf20Sopenharmony_ci	.precious_table = &apds9960_precious_table,
2128c2ecf20Sopenharmony_ci	.rd_table = &apds9960_readable_table,
2138c2ecf20Sopenharmony_ci	.wr_table = &apds9960_writeable_table,
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	.reg_defaults = apds9960_reg_defaults,
2168c2ecf20Sopenharmony_ci	.num_reg_defaults = ARRAY_SIZE(apds9960_reg_defaults),
2178c2ecf20Sopenharmony_ci	.max_register = APDS9960_REG_GFIFO_DIR(RIGHT),
2188c2ecf20Sopenharmony_ci	.cache_type = REGCACHE_RBTREE,
2198c2ecf20Sopenharmony_ci};
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic const struct iio_event_spec apds9960_pxs_event_spec[] = {
2228c2ecf20Sopenharmony_ci	{
2238c2ecf20Sopenharmony_ci		.type = IIO_EV_TYPE_THRESH,
2248c2ecf20Sopenharmony_ci		.dir = IIO_EV_DIR_RISING,
2258c2ecf20Sopenharmony_ci		.mask_separate = BIT(IIO_EV_INFO_VALUE) |
2268c2ecf20Sopenharmony_ci			BIT(IIO_EV_INFO_ENABLE),
2278c2ecf20Sopenharmony_ci	},
2288c2ecf20Sopenharmony_ci	{
2298c2ecf20Sopenharmony_ci		.type = IIO_EV_TYPE_THRESH,
2308c2ecf20Sopenharmony_ci		.dir = IIO_EV_DIR_FALLING,
2318c2ecf20Sopenharmony_ci		.mask_separate = BIT(IIO_EV_INFO_VALUE) |
2328c2ecf20Sopenharmony_ci			BIT(IIO_EV_INFO_ENABLE),
2338c2ecf20Sopenharmony_ci	},
2348c2ecf20Sopenharmony_ci};
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic const struct iio_event_spec apds9960_als_event_spec[] = {
2378c2ecf20Sopenharmony_ci	{
2388c2ecf20Sopenharmony_ci		.type = IIO_EV_TYPE_THRESH,
2398c2ecf20Sopenharmony_ci		.dir = IIO_EV_DIR_RISING,
2408c2ecf20Sopenharmony_ci		.mask_separate = BIT(IIO_EV_INFO_VALUE) |
2418c2ecf20Sopenharmony_ci			BIT(IIO_EV_INFO_ENABLE),
2428c2ecf20Sopenharmony_ci	},
2438c2ecf20Sopenharmony_ci	{
2448c2ecf20Sopenharmony_ci		.type = IIO_EV_TYPE_THRESH,
2458c2ecf20Sopenharmony_ci		.dir = IIO_EV_DIR_FALLING,
2468c2ecf20Sopenharmony_ci		.mask_separate = BIT(IIO_EV_INFO_VALUE) |
2478c2ecf20Sopenharmony_ci			BIT(IIO_EV_INFO_ENABLE),
2488c2ecf20Sopenharmony_ci	},
2498c2ecf20Sopenharmony_ci};
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci#define APDS9960_GESTURE_CHANNEL(_dir, _si) { \
2528c2ecf20Sopenharmony_ci	.type = IIO_PROXIMITY, \
2538c2ecf20Sopenharmony_ci	.channel = _si + 1, \
2548c2ecf20Sopenharmony_ci	.scan_index = _si, \
2558c2ecf20Sopenharmony_ci	.indexed = 1, \
2568c2ecf20Sopenharmony_ci	.scan_type = { \
2578c2ecf20Sopenharmony_ci		.sign = 'u', \
2588c2ecf20Sopenharmony_ci		.realbits = 8, \
2598c2ecf20Sopenharmony_ci		.storagebits = 8, \
2608c2ecf20Sopenharmony_ci	}, \
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci#define APDS9960_INTENSITY_CHANNEL(_colour) { \
2648c2ecf20Sopenharmony_ci	.type = IIO_INTENSITY, \
2658c2ecf20Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
2668c2ecf20Sopenharmony_ci	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
2678c2ecf20Sopenharmony_ci			BIT(IIO_CHAN_INFO_INT_TIME), \
2688c2ecf20Sopenharmony_ci	.channel2 = IIO_MOD_LIGHT_##_colour, \
2698c2ecf20Sopenharmony_ci	.address = APDS9960_REG_ALS_CHANNEL(_colour), \
2708c2ecf20Sopenharmony_ci	.modified = 1, \
2718c2ecf20Sopenharmony_ci	.scan_index = -1, \
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic const unsigned long apds9960_scan_masks[] = {0xf, 0};
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic const struct iio_chan_spec apds9960_channels[] = {
2778c2ecf20Sopenharmony_ci	{
2788c2ecf20Sopenharmony_ci		.type = IIO_PROXIMITY,
2798c2ecf20Sopenharmony_ci		.address = APDS9960_REG_PDATA,
2808c2ecf20Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
2818c2ecf20Sopenharmony_ci		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
2828c2ecf20Sopenharmony_ci		.channel = 0,
2838c2ecf20Sopenharmony_ci		.indexed = 0,
2848c2ecf20Sopenharmony_ci		.scan_index = -1,
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci		.event_spec = apds9960_pxs_event_spec,
2878c2ecf20Sopenharmony_ci		.num_event_specs = ARRAY_SIZE(apds9960_pxs_event_spec),
2888c2ecf20Sopenharmony_ci	},
2898c2ecf20Sopenharmony_ci	/* Gesture Sensor */
2908c2ecf20Sopenharmony_ci	APDS9960_GESTURE_CHANNEL(UP, 0),
2918c2ecf20Sopenharmony_ci	APDS9960_GESTURE_CHANNEL(DOWN, 1),
2928c2ecf20Sopenharmony_ci	APDS9960_GESTURE_CHANNEL(LEFT, 2),
2938c2ecf20Sopenharmony_ci	APDS9960_GESTURE_CHANNEL(RIGHT, 3),
2948c2ecf20Sopenharmony_ci	/* ALS */
2958c2ecf20Sopenharmony_ci	{
2968c2ecf20Sopenharmony_ci		.type = IIO_INTENSITY,
2978c2ecf20Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
2988c2ecf20Sopenharmony_ci		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
2998c2ecf20Sopenharmony_ci			BIT(IIO_CHAN_INFO_INT_TIME),
3008c2ecf20Sopenharmony_ci		.channel2 = IIO_MOD_LIGHT_CLEAR,
3018c2ecf20Sopenharmony_ci		.address = APDS9960_REG_ALS_CHANNEL(CLEAR),
3028c2ecf20Sopenharmony_ci		.modified = 1,
3038c2ecf20Sopenharmony_ci		.scan_index = -1,
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci		.event_spec = apds9960_als_event_spec,
3068c2ecf20Sopenharmony_ci		.num_event_specs = ARRAY_SIZE(apds9960_als_event_spec),
3078c2ecf20Sopenharmony_ci	},
3088c2ecf20Sopenharmony_ci	/* RGB Sensor */
3098c2ecf20Sopenharmony_ci	APDS9960_INTENSITY_CHANNEL(RED),
3108c2ecf20Sopenharmony_ci	APDS9960_INTENSITY_CHANNEL(GREEN),
3118c2ecf20Sopenharmony_ci	APDS9960_INTENSITY_CHANNEL(BLUE),
3128c2ecf20Sopenharmony_ci};
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci/* integration time in us */
3158c2ecf20Sopenharmony_cistatic const int apds9960_int_time[][2] = {
3168c2ecf20Sopenharmony_ci	{ 28000, 246},
3178c2ecf20Sopenharmony_ci	{100000, 219},
3188c2ecf20Sopenharmony_ci	{200000, 182},
3198c2ecf20Sopenharmony_ci	{700000,   0}
3208c2ecf20Sopenharmony_ci};
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci/* gain mapping */
3238c2ecf20Sopenharmony_cistatic const int apds9960_pxs_gain_map[] = {1, 2, 4, 8};
3248c2ecf20Sopenharmony_cistatic const int apds9960_als_gain_map[] = {1, 4, 16, 64};
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic IIO_CONST_ATTR(proximity_scale_available, "1 2 4 8");
3278c2ecf20Sopenharmony_cistatic IIO_CONST_ATTR(intensity_scale_available, "1 4 16 64");
3288c2ecf20Sopenharmony_cistatic IIO_CONST_ATTR_INT_TIME_AVAIL("0.028 0.1 0.2 0.7");
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic struct attribute *apds9960_attributes[] = {
3318c2ecf20Sopenharmony_ci	&iio_const_attr_proximity_scale_available.dev_attr.attr,
3328c2ecf20Sopenharmony_ci	&iio_const_attr_intensity_scale_available.dev_attr.attr,
3338c2ecf20Sopenharmony_ci	&iio_const_attr_integration_time_available.dev_attr.attr,
3348c2ecf20Sopenharmony_ci	NULL,
3358c2ecf20Sopenharmony_ci};
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic const struct attribute_group apds9960_attribute_group = {
3388c2ecf20Sopenharmony_ci	.attrs = apds9960_attributes,
3398c2ecf20Sopenharmony_ci};
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cistatic const struct reg_field apds9960_reg_field_int_als =
3428c2ecf20Sopenharmony_ci				REG_FIELD(APDS9960_REG_ENABLE, 4, 4);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic const struct reg_field apds9960_reg_field_int_ges =
3458c2ecf20Sopenharmony_ci				REG_FIELD(APDS9960_REG_GCONF_4, 1, 1);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic const struct reg_field apds9960_reg_field_int_pxs =
3488c2ecf20Sopenharmony_ci				REG_FIELD(APDS9960_REG_ENABLE, 5, 5);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic const struct reg_field apds9960_reg_field_enable_als =
3518c2ecf20Sopenharmony_ci				REG_FIELD(APDS9960_REG_ENABLE, 1, 1);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic const struct reg_field apds9960_reg_field_enable_ges =
3548c2ecf20Sopenharmony_ci				REG_FIELD(APDS9960_REG_ENABLE, 6, 6);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cistatic const struct reg_field apds9960_reg_field_enable_pxs =
3578c2ecf20Sopenharmony_ci				REG_FIELD(APDS9960_REG_ENABLE, 2, 2);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic int apds9960_set_it_time(struct apds9960_data *data, int val2)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	int ret = -EINVAL;
3628c2ecf20Sopenharmony_ci	int idx;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(apds9960_int_time); idx++) {
3658c2ecf20Sopenharmony_ci		if (apds9960_int_time[idx][0] == val2) {
3668c2ecf20Sopenharmony_ci			mutex_lock(&data->lock);
3678c2ecf20Sopenharmony_ci			ret = regmap_write(data->regmap, APDS9960_REG_ATIME,
3688c2ecf20Sopenharmony_ci						 apds9960_int_time[idx][1]);
3698c2ecf20Sopenharmony_ci			if (!ret)
3708c2ecf20Sopenharmony_ci				data->als_adc_int_us = val2;
3718c2ecf20Sopenharmony_ci			mutex_unlock(&data->lock);
3728c2ecf20Sopenharmony_ci			break;
3738c2ecf20Sopenharmony_ci		}
3748c2ecf20Sopenharmony_ci	}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	return ret;
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic int apds9960_set_pxs_gain(struct apds9960_data *data, int val)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	int ret = -EINVAL;
3828c2ecf20Sopenharmony_ci	int idx;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(apds9960_pxs_gain_map); idx++) {
3858c2ecf20Sopenharmony_ci		if (apds9960_pxs_gain_map[idx] == val) {
3868c2ecf20Sopenharmony_ci			/* pxs + gesture gains are mirrored */
3878c2ecf20Sopenharmony_ci			mutex_lock(&data->lock);
3888c2ecf20Sopenharmony_ci			ret = regmap_update_bits(data->regmap,
3898c2ecf20Sopenharmony_ci				APDS9960_REG_CONTROL,
3908c2ecf20Sopenharmony_ci				APDS9960_REG_CONTROL_PGAIN_MASK,
3918c2ecf20Sopenharmony_ci				idx << APDS9960_REG_CONTROL_PGAIN_MASK_SHIFT);
3928c2ecf20Sopenharmony_ci			if (ret) {
3938c2ecf20Sopenharmony_ci				mutex_unlock(&data->lock);
3948c2ecf20Sopenharmony_ci				break;
3958c2ecf20Sopenharmony_ci			}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci			ret = regmap_update_bits(data->regmap,
3988c2ecf20Sopenharmony_ci				APDS9960_REG_GCONF_2,
3998c2ecf20Sopenharmony_ci				APDS9960_REG_GCONF_2_GGAIN_MASK,
4008c2ecf20Sopenharmony_ci				idx << APDS9960_REG_GCONF_2_GGAIN_MASK_SHIFT);
4018c2ecf20Sopenharmony_ci			if (!ret)
4028c2ecf20Sopenharmony_ci				data->pxs_gain = idx;
4038c2ecf20Sopenharmony_ci			mutex_unlock(&data->lock);
4048c2ecf20Sopenharmony_ci			break;
4058c2ecf20Sopenharmony_ci		}
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	return ret;
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cistatic int apds9960_set_als_gain(struct apds9960_data *data, int val)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	int ret = -EINVAL;
4148c2ecf20Sopenharmony_ci	int idx;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(apds9960_als_gain_map); idx++) {
4178c2ecf20Sopenharmony_ci		if (apds9960_als_gain_map[idx] == val) {
4188c2ecf20Sopenharmony_ci			mutex_lock(&data->lock);
4198c2ecf20Sopenharmony_ci			ret = regmap_update_bits(data->regmap,
4208c2ecf20Sopenharmony_ci					APDS9960_REG_CONTROL,
4218c2ecf20Sopenharmony_ci					APDS9960_REG_CONTROL_AGAIN_MASK, idx);
4228c2ecf20Sopenharmony_ci			if (!ret)
4238c2ecf20Sopenharmony_ci				data->als_gain = idx;
4248c2ecf20Sopenharmony_ci			mutex_unlock(&data->lock);
4258c2ecf20Sopenharmony_ci			break;
4268c2ecf20Sopenharmony_ci		}
4278c2ecf20Sopenharmony_ci	}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	return ret;
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
4338c2ecf20Sopenharmony_cistatic int apds9960_set_power_state(struct apds9960_data *data, bool on)
4348c2ecf20Sopenharmony_ci{
4358c2ecf20Sopenharmony_ci	struct device *dev = &data->client->dev;
4368c2ecf20Sopenharmony_ci	int ret = 0;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	mutex_lock(&data->lock);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	if (on) {
4418c2ecf20Sopenharmony_ci		int suspended;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci		suspended = pm_runtime_suspended(dev);
4448c2ecf20Sopenharmony_ci		ret = pm_runtime_get_sync(dev);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci		/* Allow one integration cycle before allowing a reading */
4478c2ecf20Sopenharmony_ci		if (suspended)
4488c2ecf20Sopenharmony_ci			usleep_range(data->als_adc_int_us,
4498c2ecf20Sopenharmony_ci				     APDS9960_MAX_INT_TIME_IN_US);
4508c2ecf20Sopenharmony_ci	} else {
4518c2ecf20Sopenharmony_ci		pm_runtime_mark_last_busy(dev);
4528c2ecf20Sopenharmony_ci		ret = pm_runtime_put_autosuspend(dev);
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	mutex_unlock(&data->lock);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	return ret;
4588c2ecf20Sopenharmony_ci}
4598c2ecf20Sopenharmony_ci#else
4608c2ecf20Sopenharmony_cistatic int apds9960_set_power_state(struct apds9960_data *data, bool on)
4618c2ecf20Sopenharmony_ci{
4628c2ecf20Sopenharmony_ci	return 0;
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci#endif
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_cistatic int apds9960_read_raw(struct iio_dev *indio_dev,
4678c2ecf20Sopenharmony_ci			     struct iio_chan_spec const *chan,
4688c2ecf20Sopenharmony_ci			     int *val, int *val2, long mask)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	struct apds9960_data *data = iio_priv(indio_dev);
4718c2ecf20Sopenharmony_ci	__le16 buf;
4728c2ecf20Sopenharmony_ci	int ret = -EINVAL;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	if (data->gesture_mode_running)
4758c2ecf20Sopenharmony_ci		return -EBUSY;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	switch (mask) {
4788c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
4798c2ecf20Sopenharmony_ci		apds9960_set_power_state(data, true);
4808c2ecf20Sopenharmony_ci		switch (chan->type) {
4818c2ecf20Sopenharmony_ci		case IIO_PROXIMITY:
4828c2ecf20Sopenharmony_ci			ret = regmap_read(data->regmap, chan->address, val);
4838c2ecf20Sopenharmony_ci			if (!ret)
4848c2ecf20Sopenharmony_ci				ret = IIO_VAL_INT;
4858c2ecf20Sopenharmony_ci			break;
4868c2ecf20Sopenharmony_ci		case IIO_INTENSITY:
4878c2ecf20Sopenharmony_ci			ret = regmap_bulk_read(data->regmap, chan->address,
4888c2ecf20Sopenharmony_ci					       &buf, 2);
4898c2ecf20Sopenharmony_ci			if (!ret) {
4908c2ecf20Sopenharmony_ci				ret = IIO_VAL_INT;
4918c2ecf20Sopenharmony_ci				*val = le16_to_cpu(buf);
4928c2ecf20Sopenharmony_ci			}
4938c2ecf20Sopenharmony_ci			break;
4948c2ecf20Sopenharmony_ci		default:
4958c2ecf20Sopenharmony_ci			ret = -EINVAL;
4968c2ecf20Sopenharmony_ci		}
4978c2ecf20Sopenharmony_ci		apds9960_set_power_state(data, false);
4988c2ecf20Sopenharmony_ci		break;
4998c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_INT_TIME:
5008c2ecf20Sopenharmony_ci		/* RGB + ALS sensors only have integration time */
5018c2ecf20Sopenharmony_ci		mutex_lock(&data->lock);
5028c2ecf20Sopenharmony_ci		switch (chan->type) {
5038c2ecf20Sopenharmony_ci		case IIO_INTENSITY:
5048c2ecf20Sopenharmony_ci			*val = 0;
5058c2ecf20Sopenharmony_ci			*val2 = data->als_adc_int_us;
5068c2ecf20Sopenharmony_ci			ret = IIO_VAL_INT_PLUS_MICRO;
5078c2ecf20Sopenharmony_ci			break;
5088c2ecf20Sopenharmony_ci		default:
5098c2ecf20Sopenharmony_ci			ret = -EINVAL;
5108c2ecf20Sopenharmony_ci		}
5118c2ecf20Sopenharmony_ci		mutex_unlock(&data->lock);
5128c2ecf20Sopenharmony_ci		break;
5138c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
5148c2ecf20Sopenharmony_ci		mutex_lock(&data->lock);
5158c2ecf20Sopenharmony_ci		switch (chan->type) {
5168c2ecf20Sopenharmony_ci		case IIO_PROXIMITY:
5178c2ecf20Sopenharmony_ci			*val = apds9960_pxs_gain_map[data->pxs_gain];
5188c2ecf20Sopenharmony_ci			ret = IIO_VAL_INT;
5198c2ecf20Sopenharmony_ci			break;
5208c2ecf20Sopenharmony_ci		case IIO_INTENSITY:
5218c2ecf20Sopenharmony_ci			*val = apds9960_als_gain_map[data->als_gain];
5228c2ecf20Sopenharmony_ci			ret = IIO_VAL_INT;
5238c2ecf20Sopenharmony_ci			break;
5248c2ecf20Sopenharmony_ci		default:
5258c2ecf20Sopenharmony_ci			ret = -EINVAL;
5268c2ecf20Sopenharmony_ci		}
5278c2ecf20Sopenharmony_ci		mutex_unlock(&data->lock);
5288c2ecf20Sopenharmony_ci		break;
5298c2ecf20Sopenharmony_ci	}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	return ret;
5328c2ecf20Sopenharmony_ci};
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_cistatic int apds9960_write_raw(struct iio_dev *indio_dev,
5358c2ecf20Sopenharmony_ci			     struct iio_chan_spec const *chan,
5368c2ecf20Sopenharmony_ci			     int val, int val2, long mask)
5378c2ecf20Sopenharmony_ci{
5388c2ecf20Sopenharmony_ci	struct apds9960_data *data = iio_priv(indio_dev);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	switch (mask) {
5418c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_INT_TIME:
5428c2ecf20Sopenharmony_ci		/* RGB + ALS sensors only have int time */
5438c2ecf20Sopenharmony_ci		switch (chan->type) {
5448c2ecf20Sopenharmony_ci		case IIO_INTENSITY:
5458c2ecf20Sopenharmony_ci			if (val != 0)
5468c2ecf20Sopenharmony_ci				return -EINVAL;
5478c2ecf20Sopenharmony_ci			return apds9960_set_it_time(data, val2);
5488c2ecf20Sopenharmony_ci		default:
5498c2ecf20Sopenharmony_ci			return -EINVAL;
5508c2ecf20Sopenharmony_ci		}
5518c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
5528c2ecf20Sopenharmony_ci		if (val2 != 0)
5538c2ecf20Sopenharmony_ci			return -EINVAL;
5548c2ecf20Sopenharmony_ci		switch (chan->type) {
5558c2ecf20Sopenharmony_ci		case IIO_PROXIMITY:
5568c2ecf20Sopenharmony_ci			return apds9960_set_pxs_gain(data, val);
5578c2ecf20Sopenharmony_ci		case IIO_INTENSITY:
5588c2ecf20Sopenharmony_ci			return apds9960_set_als_gain(data, val);
5598c2ecf20Sopenharmony_ci		default:
5608c2ecf20Sopenharmony_ci			return -EINVAL;
5618c2ecf20Sopenharmony_ci		}
5628c2ecf20Sopenharmony_ci	default:
5638c2ecf20Sopenharmony_ci		return -EINVAL;
5648c2ecf20Sopenharmony_ci	};
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	return 0;
5678c2ecf20Sopenharmony_ci}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_cistatic inline int apds9960_get_thres_reg(const struct iio_chan_spec *chan,
5708c2ecf20Sopenharmony_ci					 enum iio_event_direction dir,
5718c2ecf20Sopenharmony_ci					 u8 *reg)
5728c2ecf20Sopenharmony_ci{
5738c2ecf20Sopenharmony_ci	switch (dir) {
5748c2ecf20Sopenharmony_ci	case IIO_EV_DIR_RISING:
5758c2ecf20Sopenharmony_ci		switch (chan->type) {
5768c2ecf20Sopenharmony_ci		case IIO_PROXIMITY:
5778c2ecf20Sopenharmony_ci			*reg = APDS9960_REG_PIHT;
5788c2ecf20Sopenharmony_ci			break;
5798c2ecf20Sopenharmony_ci		case IIO_INTENSITY:
5808c2ecf20Sopenharmony_ci			*reg = APDS9960_REG_AIHTL;
5818c2ecf20Sopenharmony_ci			break;
5828c2ecf20Sopenharmony_ci		default:
5838c2ecf20Sopenharmony_ci			return -EINVAL;
5848c2ecf20Sopenharmony_ci		}
5858c2ecf20Sopenharmony_ci		break;
5868c2ecf20Sopenharmony_ci	case IIO_EV_DIR_FALLING:
5878c2ecf20Sopenharmony_ci		switch (chan->type) {
5888c2ecf20Sopenharmony_ci		case IIO_PROXIMITY:
5898c2ecf20Sopenharmony_ci			*reg = APDS9960_REG_PILT;
5908c2ecf20Sopenharmony_ci			break;
5918c2ecf20Sopenharmony_ci		case IIO_INTENSITY:
5928c2ecf20Sopenharmony_ci			*reg = APDS9960_REG_AILTL;
5938c2ecf20Sopenharmony_ci			break;
5948c2ecf20Sopenharmony_ci		default:
5958c2ecf20Sopenharmony_ci			return -EINVAL;
5968c2ecf20Sopenharmony_ci		}
5978c2ecf20Sopenharmony_ci		break;
5988c2ecf20Sopenharmony_ci	default:
5998c2ecf20Sopenharmony_ci		return -EINVAL;
6008c2ecf20Sopenharmony_ci	}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	return 0;
6038c2ecf20Sopenharmony_ci}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_cistatic int apds9960_read_event(struct iio_dev *indio_dev,
6068c2ecf20Sopenharmony_ci			       const struct iio_chan_spec *chan,
6078c2ecf20Sopenharmony_ci			       enum iio_event_type type,
6088c2ecf20Sopenharmony_ci			       enum iio_event_direction dir,
6098c2ecf20Sopenharmony_ci			       enum iio_event_info info,
6108c2ecf20Sopenharmony_ci			       int *val, int *val2)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	u8 reg;
6138c2ecf20Sopenharmony_ci	__le16 buf;
6148c2ecf20Sopenharmony_ci	int ret = 0;
6158c2ecf20Sopenharmony_ci	struct apds9960_data *data = iio_priv(indio_dev);
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	if (info != IIO_EV_INFO_VALUE)
6188c2ecf20Sopenharmony_ci		return -EINVAL;
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	ret = apds9960_get_thres_reg(chan, dir, &reg);
6218c2ecf20Sopenharmony_ci	if (ret < 0)
6228c2ecf20Sopenharmony_ci		return ret;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	if (chan->type == IIO_PROXIMITY) {
6258c2ecf20Sopenharmony_ci		ret = regmap_read(data->regmap, reg, val);
6268c2ecf20Sopenharmony_ci		if (ret < 0)
6278c2ecf20Sopenharmony_ci			return ret;
6288c2ecf20Sopenharmony_ci	} else if (chan->type == IIO_INTENSITY) {
6298c2ecf20Sopenharmony_ci		ret = regmap_bulk_read(data->regmap, reg, &buf, 2);
6308c2ecf20Sopenharmony_ci		if (ret < 0)
6318c2ecf20Sopenharmony_ci			return ret;
6328c2ecf20Sopenharmony_ci		*val = le16_to_cpu(buf);
6338c2ecf20Sopenharmony_ci	} else
6348c2ecf20Sopenharmony_ci		return -EINVAL;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	*val2 = 0;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	return IIO_VAL_INT;
6398c2ecf20Sopenharmony_ci}
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_cistatic int apds9960_write_event(struct iio_dev *indio_dev,
6428c2ecf20Sopenharmony_ci				const struct iio_chan_spec *chan,
6438c2ecf20Sopenharmony_ci				enum iio_event_type type,
6448c2ecf20Sopenharmony_ci				enum iio_event_direction dir,
6458c2ecf20Sopenharmony_ci				enum iio_event_info info,
6468c2ecf20Sopenharmony_ci				int val, int val2)
6478c2ecf20Sopenharmony_ci{
6488c2ecf20Sopenharmony_ci	u8 reg;
6498c2ecf20Sopenharmony_ci	__le16 buf;
6508c2ecf20Sopenharmony_ci	int ret = 0;
6518c2ecf20Sopenharmony_ci	struct apds9960_data *data = iio_priv(indio_dev);
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	if (info != IIO_EV_INFO_VALUE)
6548c2ecf20Sopenharmony_ci		return -EINVAL;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	ret = apds9960_get_thres_reg(chan, dir, &reg);
6578c2ecf20Sopenharmony_ci	if (ret < 0)
6588c2ecf20Sopenharmony_ci		return ret;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	if (chan->type == IIO_PROXIMITY) {
6618c2ecf20Sopenharmony_ci		if (val < 0 || val > APDS9960_MAX_PXS_THRES_VAL)
6628c2ecf20Sopenharmony_ci			return -EINVAL;
6638c2ecf20Sopenharmony_ci		ret = regmap_write(data->regmap, reg, val);
6648c2ecf20Sopenharmony_ci		if (ret < 0)
6658c2ecf20Sopenharmony_ci			return ret;
6668c2ecf20Sopenharmony_ci	} else if (chan->type == IIO_INTENSITY) {
6678c2ecf20Sopenharmony_ci		if (val < 0 || val > APDS9960_MAX_ALS_THRES_VAL)
6688c2ecf20Sopenharmony_ci			return -EINVAL;
6698c2ecf20Sopenharmony_ci		buf = cpu_to_le16(val);
6708c2ecf20Sopenharmony_ci		ret = regmap_bulk_write(data->regmap, reg, &buf, 2);
6718c2ecf20Sopenharmony_ci		if (ret < 0)
6728c2ecf20Sopenharmony_ci			return ret;
6738c2ecf20Sopenharmony_ci	} else
6748c2ecf20Sopenharmony_ci		return -EINVAL;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	return 0;
6778c2ecf20Sopenharmony_ci}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_cistatic int apds9960_read_event_config(struct iio_dev *indio_dev,
6808c2ecf20Sopenharmony_ci				      const struct iio_chan_spec *chan,
6818c2ecf20Sopenharmony_ci				      enum iio_event_type type,
6828c2ecf20Sopenharmony_ci				      enum iio_event_direction dir)
6838c2ecf20Sopenharmony_ci{
6848c2ecf20Sopenharmony_ci	struct apds9960_data *data = iio_priv(indio_dev);
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	switch (chan->type) {
6878c2ecf20Sopenharmony_ci	case IIO_PROXIMITY:
6888c2ecf20Sopenharmony_ci		return data->pxs_int;
6898c2ecf20Sopenharmony_ci	case IIO_INTENSITY:
6908c2ecf20Sopenharmony_ci		return data->als_int;
6918c2ecf20Sopenharmony_ci	default:
6928c2ecf20Sopenharmony_ci		return -EINVAL;
6938c2ecf20Sopenharmony_ci	}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	return 0;
6968c2ecf20Sopenharmony_ci}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_cistatic int apds9960_write_event_config(struct iio_dev *indio_dev,
6998c2ecf20Sopenharmony_ci				       const struct iio_chan_spec *chan,
7008c2ecf20Sopenharmony_ci				       enum iio_event_type type,
7018c2ecf20Sopenharmony_ci				       enum iio_event_direction dir,
7028c2ecf20Sopenharmony_ci				       int state)
7038c2ecf20Sopenharmony_ci{
7048c2ecf20Sopenharmony_ci	struct apds9960_data *data = iio_priv(indio_dev);
7058c2ecf20Sopenharmony_ci	int ret;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	state = !!state;
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	switch (chan->type) {
7108c2ecf20Sopenharmony_ci	case IIO_PROXIMITY:
7118c2ecf20Sopenharmony_ci		if (data->pxs_int == state)
7128c2ecf20Sopenharmony_ci			return -EINVAL;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci		ret = regmap_field_write(data->reg_int_pxs, state);
7158c2ecf20Sopenharmony_ci		if (ret)
7168c2ecf20Sopenharmony_ci			return ret;
7178c2ecf20Sopenharmony_ci		data->pxs_int = state;
7188c2ecf20Sopenharmony_ci		apds9960_set_power_state(data, state);
7198c2ecf20Sopenharmony_ci		break;
7208c2ecf20Sopenharmony_ci	case IIO_INTENSITY:
7218c2ecf20Sopenharmony_ci		if (data->als_int == state)
7228c2ecf20Sopenharmony_ci			return -EINVAL;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci		ret = regmap_field_write(data->reg_int_als, state);
7258c2ecf20Sopenharmony_ci		if (ret)
7268c2ecf20Sopenharmony_ci			return ret;
7278c2ecf20Sopenharmony_ci		data->als_int = state;
7288c2ecf20Sopenharmony_ci		apds9960_set_power_state(data, state);
7298c2ecf20Sopenharmony_ci		break;
7308c2ecf20Sopenharmony_ci	default:
7318c2ecf20Sopenharmony_ci		return -EINVAL;
7328c2ecf20Sopenharmony_ci	}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	return 0;
7358c2ecf20Sopenharmony_ci}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_cistatic const struct iio_info apds9960_info = {
7388c2ecf20Sopenharmony_ci	.attrs = &apds9960_attribute_group,
7398c2ecf20Sopenharmony_ci	.read_raw = apds9960_read_raw,
7408c2ecf20Sopenharmony_ci	.write_raw = apds9960_write_raw,
7418c2ecf20Sopenharmony_ci	.read_event_value = apds9960_read_event,
7428c2ecf20Sopenharmony_ci	.write_event_value = apds9960_write_event,
7438c2ecf20Sopenharmony_ci	.read_event_config = apds9960_read_event_config,
7448c2ecf20Sopenharmony_ci	.write_event_config = apds9960_write_event_config,
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci};
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_cistatic inline int apds9660_fifo_is_empty(struct apds9960_data *data)
7498c2ecf20Sopenharmony_ci{
7508c2ecf20Sopenharmony_ci	int cnt;
7518c2ecf20Sopenharmony_ci	int ret;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	ret = regmap_read(data->regmap, APDS9960_REG_GFLVL, &cnt);
7548c2ecf20Sopenharmony_ci	if (ret)
7558c2ecf20Sopenharmony_ci		return ret;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	return cnt;
7588c2ecf20Sopenharmony_ci}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_cistatic void apds9960_read_gesture_fifo(struct apds9960_data *data)
7618c2ecf20Sopenharmony_ci{
7628c2ecf20Sopenharmony_ci	int ret, cnt = 0;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	mutex_lock(&data->lock);
7658c2ecf20Sopenharmony_ci	data->gesture_mode_running = 1;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	while (cnt || (cnt = apds9660_fifo_is_empty(data) > 0)) {
7688c2ecf20Sopenharmony_ci		ret = regmap_bulk_read(data->regmap, APDS9960_REG_GFIFO_BASE,
7698c2ecf20Sopenharmony_ci				      &data->buffer, 4);
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci		if (ret)
7728c2ecf20Sopenharmony_ci			goto err_read;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci		iio_push_to_buffers(data->indio_dev, data->buffer);
7758c2ecf20Sopenharmony_ci		cnt--;
7768c2ecf20Sopenharmony_ci	}
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_cierr_read:
7798c2ecf20Sopenharmony_ci	data->gesture_mode_running = 0;
7808c2ecf20Sopenharmony_ci	mutex_unlock(&data->lock);
7818c2ecf20Sopenharmony_ci}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_cistatic irqreturn_t apds9960_interrupt_handler(int irq, void *private)
7848c2ecf20Sopenharmony_ci{
7858c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev = private;
7868c2ecf20Sopenharmony_ci	struct apds9960_data *data = iio_priv(indio_dev);
7878c2ecf20Sopenharmony_ci	int ret, status;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	ret = regmap_read(data->regmap, APDS9960_REG_STATUS, &status);
7908c2ecf20Sopenharmony_ci	if (ret < 0) {
7918c2ecf20Sopenharmony_ci		dev_err(&data->client->dev, "irq status reg read failed\n");
7928c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
7938c2ecf20Sopenharmony_ci	}
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	if ((status & APDS9960_REG_STATUS_ALS_INT) && data->als_int) {
7968c2ecf20Sopenharmony_ci		iio_push_event(indio_dev,
7978c2ecf20Sopenharmony_ci			       IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0,
7988c2ecf20Sopenharmony_ci						    IIO_EV_TYPE_THRESH,
7998c2ecf20Sopenharmony_ci						    IIO_EV_DIR_EITHER),
8008c2ecf20Sopenharmony_ci			       iio_get_time_ns(indio_dev));
8018c2ecf20Sopenharmony_ci		regmap_write(data->regmap, APDS9960_REG_CICLEAR, 1);
8028c2ecf20Sopenharmony_ci	}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	if ((status & APDS9960_REG_STATUS_PS_INT) && data->pxs_int) {
8058c2ecf20Sopenharmony_ci		iio_push_event(indio_dev,
8068c2ecf20Sopenharmony_ci			       IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
8078c2ecf20Sopenharmony_ci						    IIO_EV_TYPE_THRESH,
8088c2ecf20Sopenharmony_ci						    IIO_EV_DIR_EITHER),
8098c2ecf20Sopenharmony_ci			       iio_get_time_ns(indio_dev));
8108c2ecf20Sopenharmony_ci		regmap_write(data->regmap, APDS9960_REG_PICLEAR, 1);
8118c2ecf20Sopenharmony_ci	}
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	if (status & APDS9960_REG_STATUS_GINT)
8148c2ecf20Sopenharmony_ci		apds9960_read_gesture_fifo(data);
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
8178c2ecf20Sopenharmony_ci}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_cistatic int apds9960_set_powermode(struct apds9960_data *data, bool state)
8208c2ecf20Sopenharmony_ci{
8218c2ecf20Sopenharmony_ci	return regmap_update_bits(data->regmap, APDS9960_REG_ENABLE, 1, state);
8228c2ecf20Sopenharmony_ci}
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_cistatic int apds9960_buffer_postenable(struct iio_dev *indio_dev)
8258c2ecf20Sopenharmony_ci{
8268c2ecf20Sopenharmony_ci	struct apds9960_data *data = iio_priv(indio_dev);
8278c2ecf20Sopenharmony_ci	int ret;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	ret = regmap_field_write(data->reg_int_ges, 1);
8308c2ecf20Sopenharmony_ci	if (ret)
8318c2ecf20Sopenharmony_ci		return ret;
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	ret = regmap_field_write(data->reg_enable_ges, 1);
8348c2ecf20Sopenharmony_ci	if (ret)
8358c2ecf20Sopenharmony_ci		return ret;
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	pm_runtime_get_sync(&data->client->dev);
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	return 0;
8408c2ecf20Sopenharmony_ci}
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_cistatic int apds9960_buffer_predisable(struct iio_dev *indio_dev)
8438c2ecf20Sopenharmony_ci{
8448c2ecf20Sopenharmony_ci	struct apds9960_data *data = iio_priv(indio_dev);
8458c2ecf20Sopenharmony_ci	int ret;
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	ret = regmap_field_write(data->reg_enable_ges, 0);
8488c2ecf20Sopenharmony_ci	if (ret)
8498c2ecf20Sopenharmony_ci		return ret;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	ret = regmap_field_write(data->reg_int_ges, 0);
8528c2ecf20Sopenharmony_ci	if (ret)
8538c2ecf20Sopenharmony_ci		return ret;
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	pm_runtime_put_autosuspend(&data->client->dev);
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	return 0;
8588c2ecf20Sopenharmony_ci}
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_cistatic const struct iio_buffer_setup_ops apds9960_buffer_setup_ops = {
8618c2ecf20Sopenharmony_ci	.postenable = apds9960_buffer_postenable,
8628c2ecf20Sopenharmony_ci	.predisable = apds9960_buffer_predisable,
8638c2ecf20Sopenharmony_ci};
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_cistatic int apds9960_regfield_init(struct apds9960_data *data)
8668c2ecf20Sopenharmony_ci{
8678c2ecf20Sopenharmony_ci	struct device *dev = &data->client->dev;
8688c2ecf20Sopenharmony_ci	struct regmap *regmap = data->regmap;
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	data->reg_int_als = devm_regmap_field_alloc(dev, regmap,
8718c2ecf20Sopenharmony_ci						apds9960_reg_field_int_als);
8728c2ecf20Sopenharmony_ci	if (IS_ERR(data->reg_int_als)) {
8738c2ecf20Sopenharmony_ci		dev_err(dev, "INT ALS reg field init failed\n");
8748c2ecf20Sopenharmony_ci		return PTR_ERR(data->reg_int_als);
8758c2ecf20Sopenharmony_ci	}
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	data->reg_int_ges = devm_regmap_field_alloc(dev, regmap,
8788c2ecf20Sopenharmony_ci						apds9960_reg_field_int_ges);
8798c2ecf20Sopenharmony_ci	if (IS_ERR(data->reg_int_ges)) {
8808c2ecf20Sopenharmony_ci		dev_err(dev, "INT gesture reg field init failed\n");
8818c2ecf20Sopenharmony_ci		return PTR_ERR(data->reg_int_ges);
8828c2ecf20Sopenharmony_ci	}
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	data->reg_int_pxs = devm_regmap_field_alloc(dev, regmap,
8858c2ecf20Sopenharmony_ci						apds9960_reg_field_int_pxs);
8868c2ecf20Sopenharmony_ci	if (IS_ERR(data->reg_int_pxs)) {
8878c2ecf20Sopenharmony_ci		dev_err(dev, "INT pxs reg field init failed\n");
8888c2ecf20Sopenharmony_ci		return PTR_ERR(data->reg_int_pxs);
8898c2ecf20Sopenharmony_ci	}
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	data->reg_enable_als = devm_regmap_field_alloc(dev, regmap,
8928c2ecf20Sopenharmony_ci						apds9960_reg_field_enable_als);
8938c2ecf20Sopenharmony_ci	if (IS_ERR(data->reg_enable_als)) {
8948c2ecf20Sopenharmony_ci		dev_err(dev, "Enable ALS reg field init failed\n");
8958c2ecf20Sopenharmony_ci		return PTR_ERR(data->reg_enable_als);
8968c2ecf20Sopenharmony_ci	}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	data->reg_enable_ges = devm_regmap_field_alloc(dev, regmap,
8998c2ecf20Sopenharmony_ci						apds9960_reg_field_enable_ges);
9008c2ecf20Sopenharmony_ci	if (IS_ERR(data->reg_enable_ges)) {
9018c2ecf20Sopenharmony_ci		dev_err(dev, "Enable gesture reg field init failed\n");
9028c2ecf20Sopenharmony_ci		return PTR_ERR(data->reg_enable_ges);
9038c2ecf20Sopenharmony_ci	}
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	data->reg_enable_pxs = devm_regmap_field_alloc(dev, regmap,
9068c2ecf20Sopenharmony_ci						apds9960_reg_field_enable_pxs);
9078c2ecf20Sopenharmony_ci	if (IS_ERR(data->reg_enable_pxs)) {
9088c2ecf20Sopenharmony_ci		dev_err(dev, "Enable PXS reg field init failed\n");
9098c2ecf20Sopenharmony_ci		return PTR_ERR(data->reg_enable_pxs);
9108c2ecf20Sopenharmony_ci	}
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	return 0;
9138c2ecf20Sopenharmony_ci}
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_cistatic int apds9960_chip_init(struct apds9960_data *data)
9168c2ecf20Sopenharmony_ci{
9178c2ecf20Sopenharmony_ci	int ret;
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	/* Default IT for ALS of 28 ms */
9208c2ecf20Sopenharmony_ci	ret = apds9960_set_it_time(data, 28000);
9218c2ecf20Sopenharmony_ci	if (ret)
9228c2ecf20Sopenharmony_ci		return ret;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	/* Ensure gesture interrupt is OFF */
9258c2ecf20Sopenharmony_ci	ret = regmap_field_write(data->reg_int_ges, 0);
9268c2ecf20Sopenharmony_ci	if (ret)
9278c2ecf20Sopenharmony_ci		return ret;
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	/* Disable gesture sensor, since polling is useless from user-space */
9308c2ecf20Sopenharmony_ci	ret = regmap_field_write(data->reg_enable_ges, 0);
9318c2ecf20Sopenharmony_ci	if (ret)
9328c2ecf20Sopenharmony_ci		return ret;
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	/* Ensure proximity interrupt is OFF */
9358c2ecf20Sopenharmony_ci	ret = regmap_field_write(data->reg_int_pxs, 0);
9368c2ecf20Sopenharmony_ci	if (ret)
9378c2ecf20Sopenharmony_ci		return ret;
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	/* Enable proximity sensor for polling */
9408c2ecf20Sopenharmony_ci	ret = regmap_field_write(data->reg_enable_pxs, 1);
9418c2ecf20Sopenharmony_ci	if (ret)
9428c2ecf20Sopenharmony_ci		return ret;
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	/* Ensure ALS interrupt is OFF */
9458c2ecf20Sopenharmony_ci	ret = regmap_field_write(data->reg_int_als, 0);
9468c2ecf20Sopenharmony_ci	if (ret)
9478c2ecf20Sopenharmony_ci		return ret;
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	/* Enable ALS sensor for polling */
9508c2ecf20Sopenharmony_ci	ret = regmap_field_write(data->reg_enable_als, 1);
9518c2ecf20Sopenharmony_ci	if (ret)
9528c2ecf20Sopenharmony_ci		return ret;
9538c2ecf20Sopenharmony_ci	/*
9548c2ecf20Sopenharmony_ci	 * When enabled trigger an interrupt after 3 readings
9558c2ecf20Sopenharmony_ci	 * outside threshold for ALS + PXS
9568c2ecf20Sopenharmony_ci	 */
9578c2ecf20Sopenharmony_ci	ret = regmap_write(data->regmap, APDS9960_REG_PERS,
9588c2ecf20Sopenharmony_ci			   APDS9960_DEFAULT_PERS);
9598c2ecf20Sopenharmony_ci	if (ret)
9608c2ecf20Sopenharmony_ci		return ret;
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	/*
9638c2ecf20Sopenharmony_ci	 * Wait for 4 event outside gesture threshold to prevent interrupt
9648c2ecf20Sopenharmony_ci	 * flooding.
9658c2ecf20Sopenharmony_ci	 */
9668c2ecf20Sopenharmony_ci	ret = regmap_update_bits(data->regmap, APDS9960_REG_GCONF_1,
9678c2ecf20Sopenharmony_ci			APDS9960_REG_GCONF_1_GFIFO_THRES_MASK,
9688c2ecf20Sopenharmony_ci			BIT(0) << APDS9960_REG_GCONF_1_GFIFO_THRES_MASK_SHIFT);
9698c2ecf20Sopenharmony_ci	if (ret)
9708c2ecf20Sopenharmony_ci		return ret;
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	/* Default ENTER and EXIT thresholds for the GESTURE engine. */
9738c2ecf20Sopenharmony_ci	ret = regmap_write(data->regmap, APDS9960_REG_GPENTH,
9748c2ecf20Sopenharmony_ci			   APDS9960_DEFAULT_GPENTH);
9758c2ecf20Sopenharmony_ci	if (ret)
9768c2ecf20Sopenharmony_ci		return ret;
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	ret = regmap_write(data->regmap, APDS9960_REG_GEXTH,
9798c2ecf20Sopenharmony_ci			   APDS9960_DEFAULT_GEXTH);
9808c2ecf20Sopenharmony_ci	if (ret)
9818c2ecf20Sopenharmony_ci		return ret;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	return apds9960_set_powermode(data, 1);
9848c2ecf20Sopenharmony_ci}
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_cistatic int apds9960_probe(struct i2c_client *client,
9878c2ecf20Sopenharmony_ci			  const struct i2c_device_id *id)
9888c2ecf20Sopenharmony_ci{
9898c2ecf20Sopenharmony_ci	struct apds9960_data *data;
9908c2ecf20Sopenharmony_ci	struct iio_buffer *buffer;
9918c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev;
9928c2ecf20Sopenharmony_ci	int ret;
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
9958c2ecf20Sopenharmony_ci	if (!indio_dev)
9968c2ecf20Sopenharmony_ci		return -ENOMEM;
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	buffer = devm_iio_kfifo_allocate(&client->dev);
9998c2ecf20Sopenharmony_ci	if (!buffer)
10008c2ecf20Sopenharmony_ci		return -ENOMEM;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	iio_device_attach_buffer(indio_dev, buffer);
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	indio_dev->info = &apds9960_info;
10058c2ecf20Sopenharmony_ci	indio_dev->name = APDS9960_DRV_NAME;
10068c2ecf20Sopenharmony_ci	indio_dev->channels = apds9960_channels;
10078c2ecf20Sopenharmony_ci	indio_dev->num_channels = ARRAY_SIZE(apds9960_channels);
10088c2ecf20Sopenharmony_ci	indio_dev->available_scan_masks = apds9960_scan_masks;
10098c2ecf20Sopenharmony_ci	indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
10108c2ecf20Sopenharmony_ci	indio_dev->setup_ops = &apds9960_buffer_setup_ops;
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	data = iio_priv(indio_dev);
10138c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, indio_dev);
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	data->regmap = devm_regmap_init_i2c(client, &apds9960_regmap_config);
10168c2ecf20Sopenharmony_ci	if (IS_ERR(data->regmap)) {
10178c2ecf20Sopenharmony_ci		dev_err(&client->dev, "regmap initialization failed.\n");
10188c2ecf20Sopenharmony_ci		return PTR_ERR(data->regmap);
10198c2ecf20Sopenharmony_ci	}
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	data->client = client;
10228c2ecf20Sopenharmony_ci	data->indio_dev = indio_dev;
10238c2ecf20Sopenharmony_ci	mutex_init(&data->lock);
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	ret = pm_runtime_set_active(&client->dev);
10268c2ecf20Sopenharmony_ci	if (ret)
10278c2ecf20Sopenharmony_ci		goto error_power_down;
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	pm_runtime_enable(&client->dev);
10308c2ecf20Sopenharmony_ci	pm_runtime_set_autosuspend_delay(&client->dev, 5000);
10318c2ecf20Sopenharmony_ci	pm_runtime_use_autosuspend(&client->dev);
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	apds9960_set_power_state(data, true);
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	ret = apds9960_regfield_init(data);
10368c2ecf20Sopenharmony_ci	if (ret)
10378c2ecf20Sopenharmony_ci		goto error_power_down;
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	ret = apds9960_chip_init(data);
10408c2ecf20Sopenharmony_ci	if (ret)
10418c2ecf20Sopenharmony_ci		goto error_power_down;
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci	if (client->irq <= 0) {
10448c2ecf20Sopenharmony_ci		dev_err(&client->dev, "no valid irq defined\n");
10458c2ecf20Sopenharmony_ci		ret = -EINVAL;
10468c2ecf20Sopenharmony_ci		goto error_power_down;
10478c2ecf20Sopenharmony_ci	}
10488c2ecf20Sopenharmony_ci	ret = devm_request_threaded_irq(&client->dev, client->irq,
10498c2ecf20Sopenharmony_ci					NULL, apds9960_interrupt_handler,
10508c2ecf20Sopenharmony_ci					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
10518c2ecf20Sopenharmony_ci					"apds9960_event",
10528c2ecf20Sopenharmony_ci					indio_dev);
10538c2ecf20Sopenharmony_ci	if (ret) {
10548c2ecf20Sopenharmony_ci		dev_err(&client->dev, "request irq (%d) failed\n", client->irq);
10558c2ecf20Sopenharmony_ci		goto error_power_down;
10568c2ecf20Sopenharmony_ci	}
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	ret = iio_device_register(indio_dev);
10598c2ecf20Sopenharmony_ci	if (ret)
10608c2ecf20Sopenharmony_ci		goto error_power_down;
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci	apds9960_set_power_state(data, false);
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	return 0;
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_cierror_power_down:
10678c2ecf20Sopenharmony_ci	apds9960_set_power_state(data, false);
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	return ret;
10708c2ecf20Sopenharmony_ci}
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_cistatic int apds9960_remove(struct i2c_client *client)
10738c2ecf20Sopenharmony_ci{
10748c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(client);
10758c2ecf20Sopenharmony_ci	struct apds9960_data *data = iio_priv(indio_dev);
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	iio_device_unregister(indio_dev);
10788c2ecf20Sopenharmony_ci	pm_runtime_disable(&client->dev);
10798c2ecf20Sopenharmony_ci	pm_runtime_set_suspended(&client->dev);
10808c2ecf20Sopenharmony_ci	apds9960_set_powermode(data, 0);
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	return 0;
10838c2ecf20Sopenharmony_ci}
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
10868c2ecf20Sopenharmony_cistatic int apds9960_runtime_suspend(struct device *dev)
10878c2ecf20Sopenharmony_ci{
10888c2ecf20Sopenharmony_ci	struct apds9960_data *data =
10898c2ecf20Sopenharmony_ci			iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	return apds9960_set_powermode(data, 0);
10928c2ecf20Sopenharmony_ci}
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_cistatic int apds9960_runtime_resume(struct device *dev)
10958c2ecf20Sopenharmony_ci{
10968c2ecf20Sopenharmony_ci	struct apds9960_data *data =
10978c2ecf20Sopenharmony_ci			iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci	return apds9960_set_powermode(data, 1);
11008c2ecf20Sopenharmony_ci}
11018c2ecf20Sopenharmony_ci#endif
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_cistatic const struct dev_pm_ops apds9960_pm_ops = {
11048c2ecf20Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
11058c2ecf20Sopenharmony_ci				pm_runtime_force_resume)
11068c2ecf20Sopenharmony_ci	SET_RUNTIME_PM_OPS(apds9960_runtime_suspend,
11078c2ecf20Sopenharmony_ci			   apds9960_runtime_resume, NULL)
11088c2ecf20Sopenharmony_ci};
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_cistatic const struct i2c_device_id apds9960_id[] = {
11118c2ecf20Sopenharmony_ci	{ "apds9960", 0 },
11128c2ecf20Sopenharmony_ci	{}
11138c2ecf20Sopenharmony_ci};
11148c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, apds9960_id);
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_cistatic const struct of_device_id apds9960_of_match[] = {
11178c2ecf20Sopenharmony_ci	{ .compatible = "avago,apds9960" },
11188c2ecf20Sopenharmony_ci	{ }
11198c2ecf20Sopenharmony_ci};
11208c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, apds9960_of_match);
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_cistatic struct i2c_driver apds9960_driver = {
11238c2ecf20Sopenharmony_ci	.driver = {
11248c2ecf20Sopenharmony_ci		.name	= APDS9960_DRV_NAME,
11258c2ecf20Sopenharmony_ci		.of_match_table = apds9960_of_match,
11268c2ecf20Sopenharmony_ci		.pm	= &apds9960_pm_ops,
11278c2ecf20Sopenharmony_ci	},
11288c2ecf20Sopenharmony_ci	.probe		= apds9960_probe,
11298c2ecf20Sopenharmony_ci	.remove		= apds9960_remove,
11308c2ecf20Sopenharmony_ci	.id_table	= apds9960_id,
11318c2ecf20Sopenharmony_ci};
11328c2ecf20Sopenharmony_cimodule_i2c_driver(apds9960_driver);
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ciMODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
11358c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("APDS9960 Gesture/RGB/ALS/Proximity sensor");
11368c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1137