18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * This file is part of the APDS990x sensor driver.
48c2ecf20Sopenharmony_ci * Chip is combined proximity and ambient light sensor.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Contact: Samu Onkalo <samu.p.onkalo@nokia.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/i2c.h>
148c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
158c2ecf20Sopenharmony_ci#include <linux/mutex.h>
168c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
178c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
188c2ecf20Sopenharmony_ci#include <linux/delay.h>
198c2ecf20Sopenharmony_ci#include <linux/wait.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci#include <linux/platform_data/apds990x.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/* Register map */
248c2ecf20Sopenharmony_ci#define APDS990X_ENABLE	 0x00 /* Enable of states and interrupts */
258c2ecf20Sopenharmony_ci#define APDS990X_ATIME	 0x01 /* ALS ADC time  */
268c2ecf20Sopenharmony_ci#define APDS990X_PTIME	 0x02 /* Proximity ADC time  */
278c2ecf20Sopenharmony_ci#define APDS990X_WTIME	 0x03 /* Wait time  */
288c2ecf20Sopenharmony_ci#define APDS990X_AILTL	 0x04 /* ALS interrupt low threshold low byte */
298c2ecf20Sopenharmony_ci#define APDS990X_AILTH	 0x05 /* ALS interrupt low threshold hi byte */
308c2ecf20Sopenharmony_ci#define APDS990X_AIHTL	 0x06 /* ALS interrupt hi threshold low byte */
318c2ecf20Sopenharmony_ci#define APDS990X_AIHTH	 0x07 /* ALS interrupt hi threshold hi byte */
328c2ecf20Sopenharmony_ci#define APDS990X_PILTL	 0x08 /* Proximity interrupt low threshold low byte */
338c2ecf20Sopenharmony_ci#define APDS990X_PILTH	 0x09 /* Proximity interrupt low threshold hi byte */
348c2ecf20Sopenharmony_ci#define APDS990X_PIHTL	 0x0a /* Proximity interrupt hi threshold low byte */
358c2ecf20Sopenharmony_ci#define APDS990X_PIHTH	 0x0b /* Proximity interrupt hi threshold hi byte */
368c2ecf20Sopenharmony_ci#define APDS990X_PERS	 0x0c /* Interrupt persistence filters */
378c2ecf20Sopenharmony_ci#define APDS990X_CONFIG	 0x0d /* Configuration */
388c2ecf20Sopenharmony_ci#define APDS990X_PPCOUNT 0x0e /* Proximity pulse count */
398c2ecf20Sopenharmony_ci#define APDS990X_CONTROL 0x0f /* Gain control register */
408c2ecf20Sopenharmony_ci#define APDS990X_REV	 0x11 /* Revision Number */
418c2ecf20Sopenharmony_ci#define APDS990X_ID	 0x12 /* Device ID */
428c2ecf20Sopenharmony_ci#define APDS990X_STATUS	 0x13 /* Device status */
438c2ecf20Sopenharmony_ci#define APDS990X_CDATAL	 0x14 /* Clear ADC low data register */
448c2ecf20Sopenharmony_ci#define APDS990X_CDATAH	 0x15 /* Clear ADC high data register */
458c2ecf20Sopenharmony_ci#define APDS990X_IRDATAL 0x16 /* IR ADC low data register */
468c2ecf20Sopenharmony_ci#define APDS990X_IRDATAH 0x17 /* IR ADC high data register */
478c2ecf20Sopenharmony_ci#define APDS990X_PDATAL	 0x18 /* Proximity ADC low data register */
488c2ecf20Sopenharmony_ci#define APDS990X_PDATAH	 0x19 /* Proximity ADC high data register */
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/* Control */
518c2ecf20Sopenharmony_ci#define APDS990X_MAX_AGAIN	3
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/* Enable register */
548c2ecf20Sopenharmony_ci#define APDS990X_EN_PIEN	(0x1 << 5)
558c2ecf20Sopenharmony_ci#define APDS990X_EN_AIEN	(0x1 << 4)
568c2ecf20Sopenharmony_ci#define APDS990X_EN_WEN		(0x1 << 3)
578c2ecf20Sopenharmony_ci#define APDS990X_EN_PEN		(0x1 << 2)
588c2ecf20Sopenharmony_ci#define APDS990X_EN_AEN		(0x1 << 1)
598c2ecf20Sopenharmony_ci#define APDS990X_EN_PON		(0x1 << 0)
608c2ecf20Sopenharmony_ci#define APDS990X_EN_DISABLE_ALL 0
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* Status register */
638c2ecf20Sopenharmony_ci#define APDS990X_ST_PINT	(0x1 << 5)
648c2ecf20Sopenharmony_ci#define APDS990X_ST_AINT	(0x1 << 4)
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/* I2C access types */
678c2ecf20Sopenharmony_ci#define APDS990x_CMD_TYPE_MASK	(0x03 << 5)
688c2ecf20Sopenharmony_ci#define APDS990x_CMD_TYPE_RB	(0x00 << 5) /* Repeated byte */
698c2ecf20Sopenharmony_ci#define APDS990x_CMD_TYPE_INC	(0x01 << 5) /* Auto increment */
708c2ecf20Sopenharmony_ci#define APDS990x_CMD_TYPE_SPE	(0x03 << 5) /* Special function */
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci#define APDS990x_ADDR_SHIFT	0
738c2ecf20Sopenharmony_ci#define APDS990x_CMD		0x80
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci/* Interrupt ack commands */
768c2ecf20Sopenharmony_ci#define APDS990X_INT_ACK_ALS	0x6
778c2ecf20Sopenharmony_ci#define APDS990X_INT_ACK_PS	0x5
788c2ecf20Sopenharmony_ci#define APDS990X_INT_ACK_BOTH	0x7
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/* ptime */
818c2ecf20Sopenharmony_ci#define APDS990X_PTIME_DEFAULT	0xff /* Recommended conversion time 2.7ms*/
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci/* wtime */
848c2ecf20Sopenharmony_ci#define APDS990X_WTIME_DEFAULT	0xee /* ~50ms wait time */
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci#define APDS990X_TIME_TO_ADC	1024 /* One timetick as ADC count value */
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci/* Persistence */
898c2ecf20Sopenharmony_ci#define APDS990X_APERS_SHIFT	0
908c2ecf20Sopenharmony_ci#define APDS990X_PPERS_SHIFT	4
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/* Supported ID:s */
938c2ecf20Sopenharmony_ci#define APDS990X_ID_0		0x0
948c2ecf20Sopenharmony_ci#define APDS990X_ID_4		0x4
958c2ecf20Sopenharmony_ci#define APDS990X_ID_29		0x29
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci/* pgain and pdiode settings */
988c2ecf20Sopenharmony_ci#define APDS_PGAIN_1X	       0x0
998c2ecf20Sopenharmony_ci#define APDS_PDIODE_IR	       0x2
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci#define APDS990X_LUX_OUTPUT_SCALE 10
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci/* Reverse chip factors for threshold calculation */
1048c2ecf20Sopenharmony_cistruct reverse_factors {
1058c2ecf20Sopenharmony_ci	u32 afactor;
1068c2ecf20Sopenharmony_ci	int cf1;
1078c2ecf20Sopenharmony_ci	int irf1;
1088c2ecf20Sopenharmony_ci	int cf2;
1098c2ecf20Sopenharmony_ci	int irf2;
1108c2ecf20Sopenharmony_ci};
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistruct apds990x_chip {
1138c2ecf20Sopenharmony_ci	struct apds990x_platform_data	*pdata;
1148c2ecf20Sopenharmony_ci	struct i2c_client		*client;
1158c2ecf20Sopenharmony_ci	struct mutex			mutex; /* avoid parallel access */
1168c2ecf20Sopenharmony_ci	struct regulator_bulk_data	regs[2];
1178c2ecf20Sopenharmony_ci	wait_queue_head_t		wait;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	int	prox_en;
1208c2ecf20Sopenharmony_ci	bool	prox_continuous_mode;
1218c2ecf20Sopenharmony_ci	bool	lux_wait_fresh_res;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	/* Chip parameters */
1248c2ecf20Sopenharmony_ci	struct	apds990x_chip_factors	cf;
1258c2ecf20Sopenharmony_ci	struct	reverse_factors		rcf;
1268c2ecf20Sopenharmony_ci	u16	atime;		/* als integration time */
1278c2ecf20Sopenharmony_ci	u16	arate;		/* als reporting rate */
1288c2ecf20Sopenharmony_ci	u16	a_max_result;	/* Max possible ADC value with current atime */
1298c2ecf20Sopenharmony_ci	u8	again_meas;	/* Gain used in last measurement */
1308c2ecf20Sopenharmony_ci	u8	again_next;	/* Next calculated gain */
1318c2ecf20Sopenharmony_ci	u8	pgain;
1328c2ecf20Sopenharmony_ci	u8	pdiode;
1338c2ecf20Sopenharmony_ci	u8	pdrive;
1348c2ecf20Sopenharmony_ci	u8	lux_persistence;
1358c2ecf20Sopenharmony_ci	u8	prox_persistence;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	u32	lux_raw;
1388c2ecf20Sopenharmony_ci	u32	lux;
1398c2ecf20Sopenharmony_ci	u16	lux_clear;
1408c2ecf20Sopenharmony_ci	u16	lux_ir;
1418c2ecf20Sopenharmony_ci	u16	lux_calib;
1428c2ecf20Sopenharmony_ci	u32	lux_thres_hi;
1438c2ecf20Sopenharmony_ci	u32	lux_thres_lo;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	u32	prox_thres;
1468c2ecf20Sopenharmony_ci	u16	prox_data;
1478c2ecf20Sopenharmony_ci	u16	prox_calib;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	char	chipname[10];
1508c2ecf20Sopenharmony_ci	u8	revision;
1518c2ecf20Sopenharmony_ci};
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci#define APDS_CALIB_SCALER		8192
1548c2ecf20Sopenharmony_ci#define APDS_LUX_NEUTRAL_CALIB_VALUE	(1 * APDS_CALIB_SCALER)
1558c2ecf20Sopenharmony_ci#define APDS_PROX_NEUTRAL_CALIB_VALUE	(1 * APDS_CALIB_SCALER)
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci#define APDS_PROX_DEF_THRES		600
1588c2ecf20Sopenharmony_ci#define APDS_PROX_HYSTERESIS		50
1598c2ecf20Sopenharmony_ci#define APDS_LUX_DEF_THRES_HI		101
1608c2ecf20Sopenharmony_ci#define APDS_LUX_DEF_THRES_LO		100
1618c2ecf20Sopenharmony_ci#define APDS_DEFAULT_PROX_PERS		1
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci#define APDS_TIMEOUT			2000
1648c2ecf20Sopenharmony_ci#define APDS_STARTUP_DELAY		25000 /* us */
1658c2ecf20Sopenharmony_ci#define APDS_RANGE			65535
1668c2ecf20Sopenharmony_ci#define APDS_PROX_RANGE			1023
1678c2ecf20Sopenharmony_ci#define APDS_LUX_GAIN_LO_LIMIT		100
1688c2ecf20Sopenharmony_ci#define APDS_LUX_GAIN_LO_LIMIT_STRICT	25
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci#define TIMESTEP			87 /* 2.7ms is about 87 / 32 */
1718c2ecf20Sopenharmony_ci#define TIME_STEP_SCALER		32
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci#define APDS_LUX_AVERAGING_TIME		50 /* tolerates 50/60Hz ripple */
1748c2ecf20Sopenharmony_ci#define APDS_LUX_DEFAULT_RATE		200
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic const u8 again[]	= {1, 8, 16, 120}; /* ALS gain steps */
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/* Following two tables must match i.e 10Hz rate means 1 as persistence value */
1798c2ecf20Sopenharmony_cistatic const u16 arates_hz[] = {10, 5, 2, 1};
1808c2ecf20Sopenharmony_cistatic const u8 apersis[] = {1, 2, 4, 5};
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci/* Regulators */
1838c2ecf20Sopenharmony_cistatic const char reg_vcc[] = "Vdd";
1848c2ecf20Sopenharmony_cistatic const char reg_vled[] = "Vled";
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic int apds990x_read_byte(struct apds990x_chip *chip, u8 reg, u8 *data)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct i2c_client *client = chip->client;
1898c2ecf20Sopenharmony_ci	s32 ret;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	reg &= ~APDS990x_CMD_TYPE_MASK;
1928c2ecf20Sopenharmony_ci	reg |= APDS990x_CMD | APDS990x_CMD_TYPE_RB;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	ret = i2c_smbus_read_byte_data(client, reg);
1958c2ecf20Sopenharmony_ci	*data = ret;
1968c2ecf20Sopenharmony_ci	return (int)ret;
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic int apds990x_read_word(struct apds990x_chip *chip, u8 reg, u16 *data)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct i2c_client *client = chip->client;
2028c2ecf20Sopenharmony_ci	s32 ret;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	reg &= ~APDS990x_CMD_TYPE_MASK;
2058c2ecf20Sopenharmony_ci	reg |= APDS990x_CMD | APDS990x_CMD_TYPE_INC;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	ret = i2c_smbus_read_word_data(client, reg);
2088c2ecf20Sopenharmony_ci	*data = ret;
2098c2ecf20Sopenharmony_ci	return (int)ret;
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic int apds990x_write_byte(struct apds990x_chip *chip, u8 reg, u8 data)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	struct i2c_client *client = chip->client;
2158c2ecf20Sopenharmony_ci	s32 ret;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	reg &= ~APDS990x_CMD_TYPE_MASK;
2188c2ecf20Sopenharmony_ci	reg |= APDS990x_CMD | APDS990x_CMD_TYPE_RB;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	ret = i2c_smbus_write_byte_data(client, reg, data);
2218c2ecf20Sopenharmony_ci	return (int)ret;
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic int apds990x_write_word(struct apds990x_chip *chip, u8 reg, u16 data)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	struct i2c_client *client = chip->client;
2278c2ecf20Sopenharmony_ci	s32 ret;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	reg &= ~APDS990x_CMD_TYPE_MASK;
2308c2ecf20Sopenharmony_ci	reg |= APDS990x_CMD | APDS990x_CMD_TYPE_INC;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	ret = i2c_smbus_write_word_data(client, reg, data);
2338c2ecf20Sopenharmony_ci	return (int)ret;
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic int apds990x_mode_on(struct apds990x_chip *chip)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	/* ALS is mandatory, proximity optional */
2398c2ecf20Sopenharmony_ci	u8 reg = APDS990X_EN_AIEN | APDS990X_EN_PON | APDS990X_EN_AEN |
2408c2ecf20Sopenharmony_ci		APDS990X_EN_WEN;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	if (chip->prox_en)
2438c2ecf20Sopenharmony_ci		reg |= APDS990X_EN_PIEN | APDS990X_EN_PEN;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	return apds990x_write_byte(chip, APDS990X_ENABLE, reg);
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic u16 apds990x_lux_to_threshold(struct apds990x_chip *chip, u32 lux)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	u32 thres;
2518c2ecf20Sopenharmony_ci	u32 cpl;
2528c2ecf20Sopenharmony_ci	u32 ir;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	if (lux == 0)
2558c2ecf20Sopenharmony_ci		return 0;
2568c2ecf20Sopenharmony_ci	else if (lux == APDS_RANGE)
2578c2ecf20Sopenharmony_ci		return APDS_RANGE;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	/*
2608c2ecf20Sopenharmony_ci	 * Reported LUX value is a combination of the IR and CLEAR channel
2618c2ecf20Sopenharmony_ci	 * values. However, interrupt threshold is only for clear channel.
2628c2ecf20Sopenharmony_ci	 * This function approximates needed HW threshold value for a given
2638c2ecf20Sopenharmony_ci	 * LUX value in the current lightning type.
2648c2ecf20Sopenharmony_ci	 * IR level compared to visible light varies heavily depending on the
2658c2ecf20Sopenharmony_ci	 * source of the light
2668c2ecf20Sopenharmony_ci	 *
2678c2ecf20Sopenharmony_ci	 * Calculate threshold value for the next measurement period.
2688c2ecf20Sopenharmony_ci	 * Math: threshold = lux * cpl where
2698c2ecf20Sopenharmony_ci	 * cpl = atime * again / (glass_attenuation * device_factor)
2708c2ecf20Sopenharmony_ci	 * (count-per-lux)
2718c2ecf20Sopenharmony_ci	 *
2728c2ecf20Sopenharmony_ci	 * First remove calibration. Division by four is to avoid overflow
2738c2ecf20Sopenharmony_ci	 */
2748c2ecf20Sopenharmony_ci	lux = lux * (APDS_CALIB_SCALER / 4) / (chip->lux_calib / 4);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	/* Multiplication by 64 is to increase accuracy */
2778c2ecf20Sopenharmony_ci	cpl = ((u32)chip->atime * (u32)again[chip->again_next] *
2788c2ecf20Sopenharmony_ci		APDS_PARAM_SCALE * 64) / (chip->cf.ga * chip->cf.df);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	thres = lux * cpl / 64;
2818c2ecf20Sopenharmony_ci	/*
2828c2ecf20Sopenharmony_ci	 * Convert IR light from the latest result to match with
2838c2ecf20Sopenharmony_ci	 * new gain step. This helps to adapt with the current
2848c2ecf20Sopenharmony_ci	 * source of light.
2858c2ecf20Sopenharmony_ci	 */
2868c2ecf20Sopenharmony_ci	ir = (u32)chip->lux_ir * (u32)again[chip->again_next] /
2878c2ecf20Sopenharmony_ci		(u32)again[chip->again_meas];
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	/*
2908c2ecf20Sopenharmony_ci	 * Compensate count with IR light impact
2918c2ecf20Sopenharmony_ci	 * IAC1 > IAC2 (see apds990x_get_lux for formulas)
2928c2ecf20Sopenharmony_ci	 */
2938c2ecf20Sopenharmony_ci	if (chip->lux_clear * APDS_PARAM_SCALE >=
2948c2ecf20Sopenharmony_ci		chip->rcf.afactor * chip->lux_ir)
2958c2ecf20Sopenharmony_ci		thres = (chip->rcf.cf1 * thres + chip->rcf.irf1 * ir) /
2968c2ecf20Sopenharmony_ci			APDS_PARAM_SCALE;
2978c2ecf20Sopenharmony_ci	else
2988c2ecf20Sopenharmony_ci		thres = (chip->rcf.cf2 * thres + chip->rcf.irf2 * ir) /
2998c2ecf20Sopenharmony_ci			APDS_PARAM_SCALE;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	if (thres >= chip->a_max_result)
3028c2ecf20Sopenharmony_ci		thres = chip->a_max_result - 1;
3038c2ecf20Sopenharmony_ci	return thres;
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic inline int apds990x_set_atime(struct apds990x_chip *chip, u32 time_ms)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	u8 reg_value;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	chip->atime = time_ms;
3118c2ecf20Sopenharmony_ci	/* Formula is specified in the data sheet */
3128c2ecf20Sopenharmony_ci	reg_value = 256 - ((time_ms * TIME_STEP_SCALER) / TIMESTEP);
3138c2ecf20Sopenharmony_ci	/* Calculate max ADC value for given integration time */
3148c2ecf20Sopenharmony_ci	chip->a_max_result = (u16)(256 - reg_value) * APDS990X_TIME_TO_ADC;
3158c2ecf20Sopenharmony_ci	return apds990x_write_byte(chip, APDS990X_ATIME, reg_value);
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci/* Called always with mutex locked */
3198c2ecf20Sopenharmony_cistatic int apds990x_refresh_pthres(struct apds990x_chip *chip, int data)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	int ret, lo, hi;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	/* If the chip is not in use, don't try to access it */
3248c2ecf20Sopenharmony_ci	if (pm_runtime_suspended(&chip->client->dev))
3258c2ecf20Sopenharmony_ci		return 0;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	if (data < chip->prox_thres) {
3288c2ecf20Sopenharmony_ci		lo = 0;
3298c2ecf20Sopenharmony_ci		hi = chip->prox_thres;
3308c2ecf20Sopenharmony_ci	} else {
3318c2ecf20Sopenharmony_ci		lo = chip->prox_thres - APDS_PROX_HYSTERESIS;
3328c2ecf20Sopenharmony_ci		if (chip->prox_continuous_mode)
3338c2ecf20Sopenharmony_ci			hi = chip->prox_thres;
3348c2ecf20Sopenharmony_ci		else
3358c2ecf20Sopenharmony_ci			hi = APDS_RANGE;
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	ret = apds990x_write_word(chip, APDS990X_PILTL, lo);
3398c2ecf20Sopenharmony_ci	ret |= apds990x_write_word(chip, APDS990X_PIHTL, hi);
3408c2ecf20Sopenharmony_ci	return ret;
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci/* Called always with mutex locked */
3448c2ecf20Sopenharmony_cistatic int apds990x_refresh_athres(struct apds990x_chip *chip)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	int ret;
3478c2ecf20Sopenharmony_ci	/* If the chip is not in use, don't try to access it */
3488c2ecf20Sopenharmony_ci	if (pm_runtime_suspended(&chip->client->dev))
3498c2ecf20Sopenharmony_ci		return 0;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	ret = apds990x_write_word(chip, APDS990X_AILTL,
3528c2ecf20Sopenharmony_ci			apds990x_lux_to_threshold(chip, chip->lux_thres_lo));
3538c2ecf20Sopenharmony_ci	ret |= apds990x_write_word(chip, APDS990X_AIHTL,
3548c2ecf20Sopenharmony_ci			apds990x_lux_to_threshold(chip, chip->lux_thres_hi));
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	return ret;
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci/* Called always with mutex locked */
3608c2ecf20Sopenharmony_cistatic void apds990x_force_a_refresh(struct apds990x_chip *chip)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	/* This will force ALS interrupt after the next measurement. */
3638c2ecf20Sopenharmony_ci	apds990x_write_word(chip, APDS990X_AILTL, APDS_LUX_DEF_THRES_LO);
3648c2ecf20Sopenharmony_ci	apds990x_write_word(chip, APDS990X_AIHTL, APDS_LUX_DEF_THRES_HI);
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci/* Called always with mutex locked */
3688c2ecf20Sopenharmony_cistatic void apds990x_force_p_refresh(struct apds990x_chip *chip)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	/* This will force proximity interrupt after the next measurement. */
3718c2ecf20Sopenharmony_ci	apds990x_write_word(chip, APDS990X_PILTL, APDS_PROX_DEF_THRES - 1);
3728c2ecf20Sopenharmony_ci	apds990x_write_word(chip, APDS990X_PIHTL, APDS_PROX_DEF_THRES);
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci/* Called always with mutex locked */
3768c2ecf20Sopenharmony_cistatic int apds990x_calc_again(struct apds990x_chip *chip)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	int curr_again = chip->again_meas;
3798c2ecf20Sopenharmony_ci	int next_again = chip->again_meas;
3808c2ecf20Sopenharmony_ci	int ret = 0;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	/* Calculate suitable als gain */
3838c2ecf20Sopenharmony_ci	if (chip->lux_clear == chip->a_max_result)
3848c2ecf20Sopenharmony_ci		next_again -= 2; /* ALS saturated. Decrease gain by 2 steps */
3858c2ecf20Sopenharmony_ci	else if (chip->lux_clear > chip->a_max_result / 2)
3868c2ecf20Sopenharmony_ci		next_again--;
3878c2ecf20Sopenharmony_ci	else if (chip->lux_clear < APDS_LUX_GAIN_LO_LIMIT_STRICT)
3888c2ecf20Sopenharmony_ci		next_again += 2; /* Too dark. Increase gain by 2 steps */
3898c2ecf20Sopenharmony_ci	else if (chip->lux_clear < APDS_LUX_GAIN_LO_LIMIT)
3908c2ecf20Sopenharmony_ci		next_again++;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	/* Limit gain to available range */
3938c2ecf20Sopenharmony_ci	if (next_again < 0)
3948c2ecf20Sopenharmony_ci		next_again = 0;
3958c2ecf20Sopenharmony_ci	else if (next_again > APDS990X_MAX_AGAIN)
3968c2ecf20Sopenharmony_ci		next_again = APDS990X_MAX_AGAIN;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	/* Let's check can we trust the measured result */
3998c2ecf20Sopenharmony_ci	if (chip->lux_clear == chip->a_max_result)
4008c2ecf20Sopenharmony_ci		/* Result can be totally garbage due to saturation */
4018c2ecf20Sopenharmony_ci		ret = -ERANGE;
4028c2ecf20Sopenharmony_ci	else if (next_again != curr_again &&
4038c2ecf20Sopenharmony_ci		chip->lux_clear < APDS_LUX_GAIN_LO_LIMIT_STRICT)
4048c2ecf20Sopenharmony_ci		/*
4058c2ecf20Sopenharmony_ci		 * Gain is changed and measurement result is very small.
4068c2ecf20Sopenharmony_ci		 * Result can be totally garbage due to underflow
4078c2ecf20Sopenharmony_ci		 */
4088c2ecf20Sopenharmony_ci		ret = -ERANGE;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	chip->again_next = next_again;
4118c2ecf20Sopenharmony_ci	apds990x_write_byte(chip, APDS990X_CONTROL,
4128c2ecf20Sopenharmony_ci			(chip->pdrive << 6) |
4138c2ecf20Sopenharmony_ci			(chip->pdiode << 4) |
4148c2ecf20Sopenharmony_ci			(chip->pgain << 2) |
4158c2ecf20Sopenharmony_ci			(chip->again_next << 0));
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	/*
4188c2ecf20Sopenharmony_ci	 * Error means bad result -> re-measurement is needed. The forced
4198c2ecf20Sopenharmony_ci	 * refresh uses fastest possible persistence setting to get result
4208c2ecf20Sopenharmony_ci	 * as soon as possible.
4218c2ecf20Sopenharmony_ci	 */
4228c2ecf20Sopenharmony_ci	if (ret < 0)
4238c2ecf20Sopenharmony_ci		apds990x_force_a_refresh(chip);
4248c2ecf20Sopenharmony_ci	else
4258c2ecf20Sopenharmony_ci		apds990x_refresh_athres(chip);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	return ret;
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci/* Called always with mutex locked */
4318c2ecf20Sopenharmony_cistatic int apds990x_get_lux(struct apds990x_chip *chip, int clear, int ir)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	int iac, iac1, iac2; /* IR adjusted counts */
4348c2ecf20Sopenharmony_ci	u32 lpc; /* Lux per count */
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	/* Formulas:
4378c2ecf20Sopenharmony_ci	 * iac1 = CF1 * CLEAR_CH - IRF1 * IR_CH
4388c2ecf20Sopenharmony_ci	 * iac2 = CF2 * CLEAR_CH - IRF2 * IR_CH
4398c2ecf20Sopenharmony_ci	 */
4408c2ecf20Sopenharmony_ci	iac1 = (chip->cf.cf1 * clear - chip->cf.irf1 * ir) / APDS_PARAM_SCALE;
4418c2ecf20Sopenharmony_ci	iac2 = (chip->cf.cf2 * clear - chip->cf.irf2 * ir) / APDS_PARAM_SCALE;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	iac = max(iac1, iac2);
4448c2ecf20Sopenharmony_ci	iac = max(iac, 0);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	lpc = APDS990X_LUX_OUTPUT_SCALE * (chip->cf.df * chip->cf.ga) /
4478c2ecf20Sopenharmony_ci		(u32)(again[chip->again_meas] * (u32)chip->atime);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	return (iac * lpc) / APDS_PARAM_SCALE;
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_cistatic int apds990x_ack_int(struct apds990x_chip *chip, u8 mode)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	struct i2c_client *client = chip->client;
4558c2ecf20Sopenharmony_ci	s32 ret;
4568c2ecf20Sopenharmony_ci	u8 reg = APDS990x_CMD | APDS990x_CMD_TYPE_SPE;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	switch (mode & (APDS990X_ST_AINT | APDS990X_ST_PINT)) {
4598c2ecf20Sopenharmony_ci	case APDS990X_ST_AINT:
4608c2ecf20Sopenharmony_ci		reg |= APDS990X_INT_ACK_ALS;
4618c2ecf20Sopenharmony_ci		break;
4628c2ecf20Sopenharmony_ci	case APDS990X_ST_PINT:
4638c2ecf20Sopenharmony_ci		reg |= APDS990X_INT_ACK_PS;
4648c2ecf20Sopenharmony_ci		break;
4658c2ecf20Sopenharmony_ci	default:
4668c2ecf20Sopenharmony_ci		reg |= APDS990X_INT_ACK_BOTH;
4678c2ecf20Sopenharmony_ci		break;
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	ret = i2c_smbus_read_byte_data(client, reg);
4718c2ecf20Sopenharmony_ci	return (int)ret;
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_cistatic irqreturn_t apds990x_irq(int irq, void *data)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	struct apds990x_chip *chip = data;
4778c2ecf20Sopenharmony_ci	u8 status;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	apds990x_read_byte(chip, APDS990X_STATUS, &status);
4808c2ecf20Sopenharmony_ci	apds990x_ack_int(chip, status);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
4838c2ecf20Sopenharmony_ci	if (!pm_runtime_suspended(&chip->client->dev)) {
4848c2ecf20Sopenharmony_ci		if (status & APDS990X_ST_AINT) {
4858c2ecf20Sopenharmony_ci			apds990x_read_word(chip, APDS990X_CDATAL,
4868c2ecf20Sopenharmony_ci					&chip->lux_clear);
4878c2ecf20Sopenharmony_ci			apds990x_read_word(chip, APDS990X_IRDATAL,
4888c2ecf20Sopenharmony_ci					&chip->lux_ir);
4898c2ecf20Sopenharmony_ci			/* Store used gain for calculations */
4908c2ecf20Sopenharmony_ci			chip->again_meas = chip->again_next;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci			chip->lux_raw = apds990x_get_lux(chip,
4938c2ecf20Sopenharmony_ci							chip->lux_clear,
4948c2ecf20Sopenharmony_ci							chip->lux_ir);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci			if (apds990x_calc_again(chip) == 0) {
4978c2ecf20Sopenharmony_ci				/* Result is valid */
4988c2ecf20Sopenharmony_ci				chip->lux = chip->lux_raw;
4998c2ecf20Sopenharmony_ci				chip->lux_wait_fresh_res = false;
5008c2ecf20Sopenharmony_ci				wake_up(&chip->wait);
5018c2ecf20Sopenharmony_ci				sysfs_notify(&chip->client->dev.kobj,
5028c2ecf20Sopenharmony_ci					NULL, "lux0_input");
5038c2ecf20Sopenharmony_ci			}
5048c2ecf20Sopenharmony_ci		}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci		if ((status & APDS990X_ST_PINT) && chip->prox_en) {
5078c2ecf20Sopenharmony_ci			u16 clr_ch;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci			apds990x_read_word(chip, APDS990X_CDATAL, &clr_ch);
5108c2ecf20Sopenharmony_ci			/*
5118c2ecf20Sopenharmony_ci			 * If ALS channel is saturated at min gain,
5128c2ecf20Sopenharmony_ci			 * proximity gives false posivite values.
5138c2ecf20Sopenharmony_ci			 * Just ignore them.
5148c2ecf20Sopenharmony_ci			 */
5158c2ecf20Sopenharmony_ci			if (chip->again_meas == 0 &&
5168c2ecf20Sopenharmony_ci				clr_ch == chip->a_max_result)
5178c2ecf20Sopenharmony_ci				chip->prox_data = 0;
5188c2ecf20Sopenharmony_ci			else
5198c2ecf20Sopenharmony_ci				apds990x_read_word(chip,
5208c2ecf20Sopenharmony_ci						APDS990X_PDATAL,
5218c2ecf20Sopenharmony_ci						&chip->prox_data);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci			apds990x_refresh_pthres(chip, chip->prox_data);
5248c2ecf20Sopenharmony_ci			if (chip->prox_data < chip->prox_thres)
5258c2ecf20Sopenharmony_ci				chip->prox_data = 0;
5268c2ecf20Sopenharmony_ci			else if (!chip->prox_continuous_mode)
5278c2ecf20Sopenharmony_ci				chip->prox_data = APDS_PROX_RANGE;
5288c2ecf20Sopenharmony_ci			sysfs_notify(&chip->client->dev.kobj,
5298c2ecf20Sopenharmony_ci				NULL, "prox0_raw");
5308c2ecf20Sopenharmony_ci		}
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
5338c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_cistatic int apds990x_configure(struct apds990x_chip *chip)
5378c2ecf20Sopenharmony_ci{
5388c2ecf20Sopenharmony_ci	/* It is recommended to use disabled mode during these operations */
5398c2ecf20Sopenharmony_ci	apds990x_write_byte(chip, APDS990X_ENABLE, APDS990X_EN_DISABLE_ALL);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	/* conversion and wait times for different state machince states */
5428c2ecf20Sopenharmony_ci	apds990x_write_byte(chip, APDS990X_PTIME, APDS990X_PTIME_DEFAULT);
5438c2ecf20Sopenharmony_ci	apds990x_write_byte(chip, APDS990X_WTIME, APDS990X_WTIME_DEFAULT);
5448c2ecf20Sopenharmony_ci	apds990x_set_atime(chip, APDS_LUX_AVERAGING_TIME);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	apds990x_write_byte(chip, APDS990X_CONFIG, 0);
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	/* Persistence levels */
5498c2ecf20Sopenharmony_ci	apds990x_write_byte(chip, APDS990X_PERS,
5508c2ecf20Sopenharmony_ci			(chip->lux_persistence << APDS990X_APERS_SHIFT) |
5518c2ecf20Sopenharmony_ci			(chip->prox_persistence << APDS990X_PPERS_SHIFT));
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	apds990x_write_byte(chip, APDS990X_PPCOUNT, chip->pdata->ppcount);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	/* Start with relatively small gain */
5568c2ecf20Sopenharmony_ci	chip->again_meas = 1;
5578c2ecf20Sopenharmony_ci	chip->again_next = 1;
5588c2ecf20Sopenharmony_ci	apds990x_write_byte(chip, APDS990X_CONTROL,
5598c2ecf20Sopenharmony_ci			(chip->pdrive << 6) |
5608c2ecf20Sopenharmony_ci			(chip->pdiode << 4) |
5618c2ecf20Sopenharmony_ci			(chip->pgain << 2) |
5628c2ecf20Sopenharmony_ci			(chip->again_next << 0));
5638c2ecf20Sopenharmony_ci	return 0;
5648c2ecf20Sopenharmony_ci}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_cistatic int apds990x_detect(struct apds990x_chip *chip)
5678c2ecf20Sopenharmony_ci{
5688c2ecf20Sopenharmony_ci	struct i2c_client *client = chip->client;
5698c2ecf20Sopenharmony_ci	int ret;
5708c2ecf20Sopenharmony_ci	u8 id;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	ret = apds990x_read_byte(chip, APDS990X_ID, &id);
5738c2ecf20Sopenharmony_ci	if (ret < 0) {
5748c2ecf20Sopenharmony_ci		dev_err(&client->dev, "ID read failed\n");
5758c2ecf20Sopenharmony_ci		return ret;
5768c2ecf20Sopenharmony_ci	}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	ret = apds990x_read_byte(chip, APDS990X_REV, &chip->revision);
5798c2ecf20Sopenharmony_ci	if (ret < 0) {
5808c2ecf20Sopenharmony_ci		dev_err(&client->dev, "REV read failed\n");
5818c2ecf20Sopenharmony_ci		return ret;
5828c2ecf20Sopenharmony_ci	}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	switch (id) {
5858c2ecf20Sopenharmony_ci	case APDS990X_ID_0:
5868c2ecf20Sopenharmony_ci	case APDS990X_ID_4:
5878c2ecf20Sopenharmony_ci	case APDS990X_ID_29:
5888c2ecf20Sopenharmony_ci		snprintf(chip->chipname, sizeof(chip->chipname), "APDS-990x");
5898c2ecf20Sopenharmony_ci		break;
5908c2ecf20Sopenharmony_ci	default:
5918c2ecf20Sopenharmony_ci		ret = -ENODEV;
5928c2ecf20Sopenharmony_ci		break;
5938c2ecf20Sopenharmony_ci	}
5948c2ecf20Sopenharmony_ci	return ret;
5958c2ecf20Sopenharmony_ci}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
5988c2ecf20Sopenharmony_cistatic int apds990x_chip_on(struct apds990x_chip *chip)
5998c2ecf20Sopenharmony_ci{
6008c2ecf20Sopenharmony_ci	int err	 = regulator_bulk_enable(ARRAY_SIZE(chip->regs),
6018c2ecf20Sopenharmony_ci					chip->regs);
6028c2ecf20Sopenharmony_ci	if (err < 0)
6038c2ecf20Sopenharmony_ci		return err;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	usleep_range(APDS_STARTUP_DELAY, 2 * APDS_STARTUP_DELAY);
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	/* Refresh all configs in case of regulators were off */
6088c2ecf20Sopenharmony_ci	chip->prox_data = 0;
6098c2ecf20Sopenharmony_ci	apds990x_configure(chip);
6108c2ecf20Sopenharmony_ci	apds990x_mode_on(chip);
6118c2ecf20Sopenharmony_ci	return 0;
6128c2ecf20Sopenharmony_ci}
6138c2ecf20Sopenharmony_ci#endif
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_cistatic int apds990x_chip_off(struct apds990x_chip *chip)
6168c2ecf20Sopenharmony_ci{
6178c2ecf20Sopenharmony_ci	apds990x_write_byte(chip, APDS990X_ENABLE, APDS990X_EN_DISABLE_ALL);
6188c2ecf20Sopenharmony_ci	regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs);
6198c2ecf20Sopenharmony_ci	return 0;
6208c2ecf20Sopenharmony_ci}
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_cistatic ssize_t apds990x_lux_show(struct device *dev,
6238c2ecf20Sopenharmony_ci				 struct device_attribute *attr, char *buf)
6248c2ecf20Sopenharmony_ci{
6258c2ecf20Sopenharmony_ci	struct apds990x_chip *chip = dev_get_drvdata(dev);
6268c2ecf20Sopenharmony_ci	ssize_t ret;
6278c2ecf20Sopenharmony_ci	u32 result;
6288c2ecf20Sopenharmony_ci	long timeout;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	if (pm_runtime_suspended(dev))
6318c2ecf20Sopenharmony_ci		return -EIO;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	timeout = wait_event_interruptible_timeout(chip->wait,
6348c2ecf20Sopenharmony_ci						!chip->lux_wait_fresh_res,
6358c2ecf20Sopenharmony_ci						msecs_to_jiffies(APDS_TIMEOUT));
6368c2ecf20Sopenharmony_ci	if (!timeout)
6378c2ecf20Sopenharmony_ci		return -EIO;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
6408c2ecf20Sopenharmony_ci	result = (chip->lux * chip->lux_calib) / APDS_CALIB_SCALER;
6418c2ecf20Sopenharmony_ci	if (result > (APDS_RANGE * APDS990X_LUX_OUTPUT_SCALE))
6428c2ecf20Sopenharmony_ci		result = APDS_RANGE * APDS990X_LUX_OUTPUT_SCALE;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	ret = sprintf(buf, "%d.%d\n",
6458c2ecf20Sopenharmony_ci		result / APDS990X_LUX_OUTPUT_SCALE,
6468c2ecf20Sopenharmony_ci		result % APDS990X_LUX_OUTPUT_SCALE);
6478c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
6488c2ecf20Sopenharmony_ci	return ret;
6498c2ecf20Sopenharmony_ci}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_cistatic DEVICE_ATTR(lux0_input, S_IRUGO, apds990x_lux_show, NULL);
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_cistatic ssize_t apds990x_lux_range_show(struct device *dev,
6548c2ecf20Sopenharmony_ci				 struct device_attribute *attr, char *buf)
6558c2ecf20Sopenharmony_ci{
6568c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", APDS_RANGE);
6578c2ecf20Sopenharmony_ci}
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_cistatic DEVICE_ATTR(lux0_sensor_range, S_IRUGO, apds990x_lux_range_show, NULL);
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_cistatic ssize_t apds990x_lux_calib_format_show(struct device *dev,
6628c2ecf20Sopenharmony_ci				 struct device_attribute *attr, char *buf)
6638c2ecf20Sopenharmony_ci{
6648c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", APDS_CALIB_SCALER);
6658c2ecf20Sopenharmony_ci}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_cistatic DEVICE_ATTR(lux0_calibscale_default, S_IRUGO,
6688c2ecf20Sopenharmony_ci		apds990x_lux_calib_format_show, NULL);
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_cistatic ssize_t apds990x_lux_calib_show(struct device *dev,
6718c2ecf20Sopenharmony_ci				 struct device_attribute *attr, char *buf)
6728c2ecf20Sopenharmony_ci{
6738c2ecf20Sopenharmony_ci	struct apds990x_chip *chip = dev_get_drvdata(dev);
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", chip->lux_calib);
6768c2ecf20Sopenharmony_ci}
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_cistatic ssize_t apds990x_lux_calib_store(struct device *dev,
6798c2ecf20Sopenharmony_ci				  struct device_attribute *attr,
6808c2ecf20Sopenharmony_ci				  const char *buf, size_t len)
6818c2ecf20Sopenharmony_ci{
6828c2ecf20Sopenharmony_ci	struct apds990x_chip *chip = dev_get_drvdata(dev);
6838c2ecf20Sopenharmony_ci	unsigned long value;
6848c2ecf20Sopenharmony_ci	int ret;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	ret = kstrtoul(buf, 0, &value);
6878c2ecf20Sopenharmony_ci	if (ret)
6888c2ecf20Sopenharmony_ci		return ret;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	chip->lux_calib = value;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	return len;
6938c2ecf20Sopenharmony_ci}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_cistatic DEVICE_ATTR(lux0_calibscale, S_IRUGO | S_IWUSR, apds990x_lux_calib_show,
6968c2ecf20Sopenharmony_ci		apds990x_lux_calib_store);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_cistatic ssize_t apds990x_rate_avail(struct device *dev,
6998c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf)
7008c2ecf20Sopenharmony_ci{
7018c2ecf20Sopenharmony_ci	int i;
7028c2ecf20Sopenharmony_ci	int pos = 0;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(arates_hz); i++)
7058c2ecf20Sopenharmony_ci		pos += sprintf(buf + pos, "%d ", arates_hz[i]);
7068c2ecf20Sopenharmony_ci	sprintf(buf + pos - 1, "\n");
7078c2ecf20Sopenharmony_ci	return pos;
7088c2ecf20Sopenharmony_ci}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_cistatic ssize_t apds990x_rate_show(struct device *dev,
7118c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf)
7128c2ecf20Sopenharmony_ci{
7138c2ecf20Sopenharmony_ci	struct apds990x_chip *chip =  dev_get_drvdata(dev);
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", chip->arate);
7168c2ecf20Sopenharmony_ci}
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_cistatic int apds990x_set_arate(struct apds990x_chip *chip, int rate)
7198c2ecf20Sopenharmony_ci{
7208c2ecf20Sopenharmony_ci	int i;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(arates_hz); i++)
7238c2ecf20Sopenharmony_ci		if (rate >= arates_hz[i])
7248c2ecf20Sopenharmony_ci			break;
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	if (i == ARRAY_SIZE(arates_hz))
7278c2ecf20Sopenharmony_ci		return -EINVAL;
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	/* Pick up corresponding persistence value */
7308c2ecf20Sopenharmony_ci	chip->lux_persistence = apersis[i];
7318c2ecf20Sopenharmony_ci	chip->arate = arates_hz[i];
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	/* If the chip is not in use, don't try to access it */
7348c2ecf20Sopenharmony_ci	if (pm_runtime_suspended(&chip->client->dev))
7358c2ecf20Sopenharmony_ci		return 0;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	/* Persistence levels */
7388c2ecf20Sopenharmony_ci	return apds990x_write_byte(chip, APDS990X_PERS,
7398c2ecf20Sopenharmony_ci			(chip->lux_persistence << APDS990X_APERS_SHIFT) |
7408c2ecf20Sopenharmony_ci			(chip->prox_persistence << APDS990X_PPERS_SHIFT));
7418c2ecf20Sopenharmony_ci}
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_cistatic ssize_t apds990x_rate_store(struct device *dev,
7448c2ecf20Sopenharmony_ci				  struct device_attribute *attr,
7458c2ecf20Sopenharmony_ci				  const char *buf, size_t len)
7468c2ecf20Sopenharmony_ci{
7478c2ecf20Sopenharmony_ci	struct apds990x_chip *chip =  dev_get_drvdata(dev);
7488c2ecf20Sopenharmony_ci	unsigned long value;
7498c2ecf20Sopenharmony_ci	int ret;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	ret = kstrtoul(buf, 0, &value);
7528c2ecf20Sopenharmony_ci	if (ret)
7538c2ecf20Sopenharmony_ci		return ret;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
7568c2ecf20Sopenharmony_ci	ret = apds990x_set_arate(chip, value);
7578c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	if (ret < 0)
7608c2ecf20Sopenharmony_ci		return ret;
7618c2ecf20Sopenharmony_ci	return len;
7628c2ecf20Sopenharmony_ci}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_cistatic DEVICE_ATTR(lux0_rate_avail, S_IRUGO, apds990x_rate_avail, NULL);
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_cistatic DEVICE_ATTR(lux0_rate, S_IRUGO | S_IWUSR, apds990x_rate_show,
7678c2ecf20Sopenharmony_ci						 apds990x_rate_store);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_cistatic ssize_t apds990x_prox_show(struct device *dev,
7708c2ecf20Sopenharmony_ci				 struct device_attribute *attr, char *buf)
7718c2ecf20Sopenharmony_ci{
7728c2ecf20Sopenharmony_ci	ssize_t ret;
7738c2ecf20Sopenharmony_ci	struct apds990x_chip *chip =  dev_get_drvdata(dev);
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	if (pm_runtime_suspended(dev) || !chip->prox_en)
7768c2ecf20Sopenharmony_ci		return -EIO;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
7798c2ecf20Sopenharmony_ci	ret = sprintf(buf, "%d\n", chip->prox_data);
7808c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
7818c2ecf20Sopenharmony_ci	return ret;
7828c2ecf20Sopenharmony_ci}
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_cistatic DEVICE_ATTR(prox0_raw, S_IRUGO, apds990x_prox_show, NULL);
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_cistatic ssize_t apds990x_prox_range_show(struct device *dev,
7878c2ecf20Sopenharmony_ci				 struct device_attribute *attr, char *buf)
7888c2ecf20Sopenharmony_ci{
7898c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", APDS_PROX_RANGE);
7908c2ecf20Sopenharmony_ci}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_cistatic DEVICE_ATTR(prox0_sensor_range, S_IRUGO, apds990x_prox_range_show, NULL);
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_cistatic ssize_t apds990x_prox_enable_show(struct device *dev,
7958c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf)
7968c2ecf20Sopenharmony_ci{
7978c2ecf20Sopenharmony_ci	struct apds990x_chip *chip =  dev_get_drvdata(dev);
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", chip->prox_en);
8008c2ecf20Sopenharmony_ci}
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_cistatic ssize_t apds990x_prox_enable_store(struct device *dev,
8038c2ecf20Sopenharmony_ci				  struct device_attribute *attr,
8048c2ecf20Sopenharmony_ci				  const char *buf, size_t len)
8058c2ecf20Sopenharmony_ci{
8068c2ecf20Sopenharmony_ci	struct apds990x_chip *chip =  dev_get_drvdata(dev);
8078c2ecf20Sopenharmony_ci	unsigned long value;
8088c2ecf20Sopenharmony_ci	int ret;
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	ret = kstrtoul(buf, 0, &value);
8118c2ecf20Sopenharmony_ci	if (ret)
8128c2ecf20Sopenharmony_ci		return ret;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	if (!chip->prox_en)
8178c2ecf20Sopenharmony_ci		chip->prox_data = 0;
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	if (value)
8208c2ecf20Sopenharmony_ci		chip->prox_en++;
8218c2ecf20Sopenharmony_ci	else if (chip->prox_en > 0)
8228c2ecf20Sopenharmony_ci		chip->prox_en--;
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	if (!pm_runtime_suspended(dev))
8258c2ecf20Sopenharmony_ci		apds990x_mode_on(chip);
8268c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
8278c2ecf20Sopenharmony_ci	return len;
8288c2ecf20Sopenharmony_ci}
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_cistatic DEVICE_ATTR(prox0_raw_en, S_IRUGO | S_IWUSR, apds990x_prox_enable_show,
8318c2ecf20Sopenharmony_ci						   apds990x_prox_enable_store);
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_cistatic const char *reporting_modes[] = {"trigger", "periodic"};
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_cistatic ssize_t apds990x_prox_reporting_mode_show(struct device *dev,
8368c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf)
8378c2ecf20Sopenharmony_ci{
8388c2ecf20Sopenharmony_ci	struct apds990x_chip *chip =  dev_get_drvdata(dev);
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	return sprintf(buf, "%s\n",
8418c2ecf20Sopenharmony_ci		reporting_modes[!!chip->prox_continuous_mode]);
8428c2ecf20Sopenharmony_ci}
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_cistatic ssize_t apds990x_prox_reporting_mode_store(struct device *dev,
8458c2ecf20Sopenharmony_ci				  struct device_attribute *attr,
8468c2ecf20Sopenharmony_ci				  const char *buf, size_t len)
8478c2ecf20Sopenharmony_ci{
8488c2ecf20Sopenharmony_ci	struct apds990x_chip *chip =  dev_get_drvdata(dev);
8498c2ecf20Sopenharmony_ci	int ret;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	ret = sysfs_match_string(reporting_modes, buf);
8528c2ecf20Sopenharmony_ci	if (ret < 0)
8538c2ecf20Sopenharmony_ci		return ret;
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	chip->prox_continuous_mode = ret;
8568c2ecf20Sopenharmony_ci	return len;
8578c2ecf20Sopenharmony_ci}
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_cistatic DEVICE_ATTR(prox0_reporting_mode, S_IRUGO | S_IWUSR,
8608c2ecf20Sopenharmony_ci		apds990x_prox_reporting_mode_show,
8618c2ecf20Sopenharmony_ci		apds990x_prox_reporting_mode_store);
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_cistatic ssize_t apds990x_prox_reporting_avail_show(struct device *dev,
8648c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf)
8658c2ecf20Sopenharmony_ci{
8668c2ecf20Sopenharmony_ci	return sprintf(buf, "%s %s\n", reporting_modes[0], reporting_modes[1]);
8678c2ecf20Sopenharmony_ci}
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_cistatic DEVICE_ATTR(prox0_reporting_mode_avail, S_IRUGO | S_IWUSR,
8708c2ecf20Sopenharmony_ci		apds990x_prox_reporting_avail_show, NULL);
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_cistatic ssize_t apds990x_lux_thresh_above_show(struct device *dev,
8748c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf)
8758c2ecf20Sopenharmony_ci{
8768c2ecf20Sopenharmony_ci	struct apds990x_chip *chip =  dev_get_drvdata(dev);
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", chip->lux_thres_hi);
8798c2ecf20Sopenharmony_ci}
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_cistatic ssize_t apds990x_lux_thresh_below_show(struct device *dev,
8828c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf)
8838c2ecf20Sopenharmony_ci{
8848c2ecf20Sopenharmony_ci	struct apds990x_chip *chip =  dev_get_drvdata(dev);
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", chip->lux_thres_lo);
8878c2ecf20Sopenharmony_ci}
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_cistatic ssize_t apds990x_set_lux_thresh(struct apds990x_chip *chip, u32 *target,
8908c2ecf20Sopenharmony_ci				const char *buf)
8918c2ecf20Sopenharmony_ci{
8928c2ecf20Sopenharmony_ci	unsigned long thresh;
8938c2ecf20Sopenharmony_ci	int ret;
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	ret = kstrtoul(buf, 0, &thresh);
8968c2ecf20Sopenharmony_ci	if (ret)
8978c2ecf20Sopenharmony_ci		return ret;
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	if (thresh > APDS_RANGE)
9008c2ecf20Sopenharmony_ci		return -EINVAL;
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
9038c2ecf20Sopenharmony_ci	*target = thresh;
9048c2ecf20Sopenharmony_ci	/*
9058c2ecf20Sopenharmony_ci	 * Don't update values in HW if we are still waiting for
9068c2ecf20Sopenharmony_ci	 * first interrupt to come after device handle open call.
9078c2ecf20Sopenharmony_ci	 */
9088c2ecf20Sopenharmony_ci	if (!chip->lux_wait_fresh_res)
9098c2ecf20Sopenharmony_ci		apds990x_refresh_athres(chip);
9108c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
9118c2ecf20Sopenharmony_ci	return ret;
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci}
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_cistatic ssize_t apds990x_lux_thresh_above_store(struct device *dev,
9168c2ecf20Sopenharmony_ci				  struct device_attribute *attr,
9178c2ecf20Sopenharmony_ci				  const char *buf, size_t len)
9188c2ecf20Sopenharmony_ci{
9198c2ecf20Sopenharmony_ci	struct apds990x_chip *chip =  dev_get_drvdata(dev);
9208c2ecf20Sopenharmony_ci	int ret = apds990x_set_lux_thresh(chip, &chip->lux_thres_hi, buf);
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	if (ret < 0)
9238c2ecf20Sopenharmony_ci		return ret;
9248c2ecf20Sopenharmony_ci	return len;
9258c2ecf20Sopenharmony_ci}
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_cistatic ssize_t apds990x_lux_thresh_below_store(struct device *dev,
9288c2ecf20Sopenharmony_ci				  struct device_attribute *attr,
9298c2ecf20Sopenharmony_ci				  const char *buf, size_t len)
9308c2ecf20Sopenharmony_ci{
9318c2ecf20Sopenharmony_ci	struct apds990x_chip *chip =  dev_get_drvdata(dev);
9328c2ecf20Sopenharmony_ci	int ret = apds990x_set_lux_thresh(chip, &chip->lux_thres_lo, buf);
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	if (ret < 0)
9358c2ecf20Sopenharmony_ci		return ret;
9368c2ecf20Sopenharmony_ci	return len;
9378c2ecf20Sopenharmony_ci}
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_cistatic DEVICE_ATTR(lux0_thresh_above_value, S_IRUGO | S_IWUSR,
9408c2ecf20Sopenharmony_ci		apds990x_lux_thresh_above_show,
9418c2ecf20Sopenharmony_ci		apds990x_lux_thresh_above_store);
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_cistatic DEVICE_ATTR(lux0_thresh_below_value, S_IRUGO | S_IWUSR,
9448c2ecf20Sopenharmony_ci		apds990x_lux_thresh_below_show,
9458c2ecf20Sopenharmony_ci		apds990x_lux_thresh_below_store);
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_cistatic ssize_t apds990x_prox_threshold_show(struct device *dev,
9488c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf)
9498c2ecf20Sopenharmony_ci{
9508c2ecf20Sopenharmony_ci	struct apds990x_chip *chip =  dev_get_drvdata(dev);
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", chip->prox_thres);
9538c2ecf20Sopenharmony_ci}
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_cistatic ssize_t apds990x_prox_threshold_store(struct device *dev,
9568c2ecf20Sopenharmony_ci				  struct device_attribute *attr,
9578c2ecf20Sopenharmony_ci				  const char *buf, size_t len)
9588c2ecf20Sopenharmony_ci{
9598c2ecf20Sopenharmony_ci	struct apds990x_chip *chip =  dev_get_drvdata(dev);
9608c2ecf20Sopenharmony_ci	unsigned long value;
9618c2ecf20Sopenharmony_ci	int ret;
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	ret = kstrtoul(buf, 0, &value);
9648c2ecf20Sopenharmony_ci	if (ret)
9658c2ecf20Sopenharmony_ci		return ret;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	if ((value > APDS_RANGE) || (value == 0) ||
9688c2ecf20Sopenharmony_ci		(value < APDS_PROX_HYSTERESIS))
9698c2ecf20Sopenharmony_ci		return -EINVAL;
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
9728c2ecf20Sopenharmony_ci	chip->prox_thres = value;
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	apds990x_force_p_refresh(chip);
9758c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
9768c2ecf20Sopenharmony_ci	return len;
9778c2ecf20Sopenharmony_ci}
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_cistatic DEVICE_ATTR(prox0_thresh_above_value, S_IRUGO | S_IWUSR,
9808c2ecf20Sopenharmony_ci		apds990x_prox_threshold_show,
9818c2ecf20Sopenharmony_ci		apds990x_prox_threshold_store);
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_cistatic ssize_t apds990x_power_state_show(struct device *dev,
9848c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf)
9858c2ecf20Sopenharmony_ci{
9868c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", !pm_runtime_suspended(dev));
9878c2ecf20Sopenharmony_ci	return 0;
9888c2ecf20Sopenharmony_ci}
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_cistatic ssize_t apds990x_power_state_store(struct device *dev,
9918c2ecf20Sopenharmony_ci				  struct device_attribute *attr,
9928c2ecf20Sopenharmony_ci				  const char *buf, size_t len)
9938c2ecf20Sopenharmony_ci{
9948c2ecf20Sopenharmony_ci	struct apds990x_chip *chip =  dev_get_drvdata(dev);
9958c2ecf20Sopenharmony_ci	unsigned long value;
9968c2ecf20Sopenharmony_ci	int ret;
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	ret = kstrtoul(buf, 0, &value);
9998c2ecf20Sopenharmony_ci	if (ret)
10008c2ecf20Sopenharmony_ci		return ret;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	if (value) {
10038c2ecf20Sopenharmony_ci		pm_runtime_get_sync(dev);
10048c2ecf20Sopenharmony_ci		mutex_lock(&chip->mutex);
10058c2ecf20Sopenharmony_ci		chip->lux_wait_fresh_res = true;
10068c2ecf20Sopenharmony_ci		apds990x_force_a_refresh(chip);
10078c2ecf20Sopenharmony_ci		apds990x_force_p_refresh(chip);
10088c2ecf20Sopenharmony_ci		mutex_unlock(&chip->mutex);
10098c2ecf20Sopenharmony_ci	} else {
10108c2ecf20Sopenharmony_ci		if (!pm_runtime_suspended(dev))
10118c2ecf20Sopenharmony_ci			pm_runtime_put(dev);
10128c2ecf20Sopenharmony_ci	}
10138c2ecf20Sopenharmony_ci	return len;
10148c2ecf20Sopenharmony_ci}
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_cistatic DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
10178c2ecf20Sopenharmony_ci		apds990x_power_state_show,
10188c2ecf20Sopenharmony_ci		apds990x_power_state_store);
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_cistatic ssize_t apds990x_chip_id_show(struct device *dev,
10218c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf)
10228c2ecf20Sopenharmony_ci{
10238c2ecf20Sopenharmony_ci	struct apds990x_chip *chip =  dev_get_drvdata(dev);
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	return sprintf(buf, "%s %d\n", chip->chipname, chip->revision);
10268c2ecf20Sopenharmony_ci}
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_cistatic DEVICE_ATTR(chip_id, S_IRUGO, apds990x_chip_id_show, NULL);
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_cistatic struct attribute *sysfs_attrs_ctrl[] = {
10318c2ecf20Sopenharmony_ci	&dev_attr_lux0_calibscale.attr,
10328c2ecf20Sopenharmony_ci	&dev_attr_lux0_calibscale_default.attr,
10338c2ecf20Sopenharmony_ci	&dev_attr_lux0_input.attr,
10348c2ecf20Sopenharmony_ci	&dev_attr_lux0_sensor_range.attr,
10358c2ecf20Sopenharmony_ci	&dev_attr_lux0_rate.attr,
10368c2ecf20Sopenharmony_ci	&dev_attr_lux0_rate_avail.attr,
10378c2ecf20Sopenharmony_ci	&dev_attr_lux0_thresh_above_value.attr,
10388c2ecf20Sopenharmony_ci	&dev_attr_lux0_thresh_below_value.attr,
10398c2ecf20Sopenharmony_ci	&dev_attr_prox0_raw_en.attr,
10408c2ecf20Sopenharmony_ci	&dev_attr_prox0_raw.attr,
10418c2ecf20Sopenharmony_ci	&dev_attr_prox0_sensor_range.attr,
10428c2ecf20Sopenharmony_ci	&dev_attr_prox0_thresh_above_value.attr,
10438c2ecf20Sopenharmony_ci	&dev_attr_prox0_reporting_mode.attr,
10448c2ecf20Sopenharmony_ci	&dev_attr_prox0_reporting_mode_avail.attr,
10458c2ecf20Sopenharmony_ci	&dev_attr_chip_id.attr,
10468c2ecf20Sopenharmony_ci	&dev_attr_power_state.attr,
10478c2ecf20Sopenharmony_ci	NULL
10488c2ecf20Sopenharmony_ci};
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_cistatic const struct attribute_group apds990x_attribute_group[] = {
10518c2ecf20Sopenharmony_ci	{.attrs = sysfs_attrs_ctrl },
10528c2ecf20Sopenharmony_ci};
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_cistatic int apds990x_probe(struct i2c_client *client,
10558c2ecf20Sopenharmony_ci				const struct i2c_device_id *id)
10568c2ecf20Sopenharmony_ci{
10578c2ecf20Sopenharmony_ci	struct apds990x_chip *chip;
10588c2ecf20Sopenharmony_ci	int err;
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	chip = kzalloc(sizeof *chip, GFP_KERNEL);
10618c2ecf20Sopenharmony_ci	if (!chip)
10628c2ecf20Sopenharmony_ci		return -ENOMEM;
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, chip);
10658c2ecf20Sopenharmony_ci	chip->client  = client;
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	init_waitqueue_head(&chip->wait);
10688c2ecf20Sopenharmony_ci	mutex_init(&chip->mutex);
10698c2ecf20Sopenharmony_ci	chip->pdata	= client->dev.platform_data;
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	if (chip->pdata == NULL) {
10728c2ecf20Sopenharmony_ci		dev_err(&client->dev, "platform data is mandatory\n");
10738c2ecf20Sopenharmony_ci		err = -EINVAL;
10748c2ecf20Sopenharmony_ci		goto fail1;
10758c2ecf20Sopenharmony_ci	}
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	if (chip->pdata->cf.ga == 0) {
10788c2ecf20Sopenharmony_ci		/* set uncovered sensor default parameters */
10798c2ecf20Sopenharmony_ci		chip->cf.ga = 1966; /* 0.48 * APDS_PARAM_SCALE */
10808c2ecf20Sopenharmony_ci		chip->cf.cf1 = 4096; /* 1.00 * APDS_PARAM_SCALE */
10818c2ecf20Sopenharmony_ci		chip->cf.irf1 = 9134; /* 2.23 * APDS_PARAM_SCALE */
10828c2ecf20Sopenharmony_ci		chip->cf.cf2 = 2867; /* 0.70 * APDS_PARAM_SCALE */
10838c2ecf20Sopenharmony_ci		chip->cf.irf2 = 5816; /* 1.42 * APDS_PARAM_SCALE */
10848c2ecf20Sopenharmony_ci		chip->cf.df = 52;
10858c2ecf20Sopenharmony_ci	} else {
10868c2ecf20Sopenharmony_ci		chip->cf = chip->pdata->cf;
10878c2ecf20Sopenharmony_ci	}
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	/* precalculate inverse chip factors for threshold control */
10908c2ecf20Sopenharmony_ci	chip->rcf.afactor =
10918c2ecf20Sopenharmony_ci		(chip->cf.irf1 - chip->cf.irf2) * APDS_PARAM_SCALE /
10928c2ecf20Sopenharmony_ci		(chip->cf.cf1 - chip->cf.cf2);
10938c2ecf20Sopenharmony_ci	chip->rcf.cf1 = APDS_PARAM_SCALE * APDS_PARAM_SCALE /
10948c2ecf20Sopenharmony_ci		chip->cf.cf1;
10958c2ecf20Sopenharmony_ci	chip->rcf.irf1 = chip->cf.irf1 * APDS_PARAM_SCALE /
10968c2ecf20Sopenharmony_ci		chip->cf.cf1;
10978c2ecf20Sopenharmony_ci	chip->rcf.cf2 = APDS_PARAM_SCALE * APDS_PARAM_SCALE /
10988c2ecf20Sopenharmony_ci		chip->cf.cf2;
10998c2ecf20Sopenharmony_ci	chip->rcf.irf2 = chip->cf.irf2 * APDS_PARAM_SCALE /
11008c2ecf20Sopenharmony_ci		chip->cf.cf2;
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	/* Set something to start with */
11038c2ecf20Sopenharmony_ci	chip->lux_thres_hi = APDS_LUX_DEF_THRES_HI;
11048c2ecf20Sopenharmony_ci	chip->lux_thres_lo = APDS_LUX_DEF_THRES_LO;
11058c2ecf20Sopenharmony_ci	chip->lux_calib = APDS_LUX_NEUTRAL_CALIB_VALUE;
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	chip->prox_thres = APDS_PROX_DEF_THRES;
11088c2ecf20Sopenharmony_ci	chip->pdrive = chip->pdata->pdrive;
11098c2ecf20Sopenharmony_ci	chip->pdiode = APDS_PDIODE_IR;
11108c2ecf20Sopenharmony_ci	chip->pgain = APDS_PGAIN_1X;
11118c2ecf20Sopenharmony_ci	chip->prox_calib = APDS_PROX_NEUTRAL_CALIB_VALUE;
11128c2ecf20Sopenharmony_ci	chip->prox_persistence = APDS_DEFAULT_PROX_PERS;
11138c2ecf20Sopenharmony_ci	chip->prox_continuous_mode = false;
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	chip->regs[0].supply = reg_vcc;
11168c2ecf20Sopenharmony_ci	chip->regs[1].supply = reg_vled;
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	err = regulator_bulk_get(&client->dev,
11198c2ecf20Sopenharmony_ci				 ARRAY_SIZE(chip->regs), chip->regs);
11208c2ecf20Sopenharmony_ci	if (err < 0) {
11218c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Cannot get regulators\n");
11228c2ecf20Sopenharmony_ci		goto fail1;
11238c2ecf20Sopenharmony_ci	}
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	err = regulator_bulk_enable(ARRAY_SIZE(chip->regs), chip->regs);
11268c2ecf20Sopenharmony_ci	if (err < 0) {
11278c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Cannot enable regulators\n");
11288c2ecf20Sopenharmony_ci		goto fail2;
11298c2ecf20Sopenharmony_ci	}
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci	usleep_range(APDS_STARTUP_DELAY, 2 * APDS_STARTUP_DELAY);
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	err = apds990x_detect(chip);
11348c2ecf20Sopenharmony_ci	if (err < 0) {
11358c2ecf20Sopenharmony_ci		dev_err(&client->dev, "APDS990X not found\n");
11368c2ecf20Sopenharmony_ci		goto fail3;
11378c2ecf20Sopenharmony_ci	}
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	pm_runtime_set_active(&client->dev);
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ci	apds990x_configure(chip);
11428c2ecf20Sopenharmony_ci	apds990x_set_arate(chip, APDS_LUX_DEFAULT_RATE);
11438c2ecf20Sopenharmony_ci	apds990x_mode_on(chip);
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	pm_runtime_enable(&client->dev);
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci	if (chip->pdata->setup_resources) {
11488c2ecf20Sopenharmony_ci		err = chip->pdata->setup_resources();
11498c2ecf20Sopenharmony_ci		if (err) {
11508c2ecf20Sopenharmony_ci			err = -EINVAL;
11518c2ecf20Sopenharmony_ci			goto fail3;
11528c2ecf20Sopenharmony_ci		}
11538c2ecf20Sopenharmony_ci	}
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	err = sysfs_create_group(&chip->client->dev.kobj,
11568c2ecf20Sopenharmony_ci				apds990x_attribute_group);
11578c2ecf20Sopenharmony_ci	if (err < 0) {
11588c2ecf20Sopenharmony_ci		dev_err(&chip->client->dev, "Sysfs registration failed\n");
11598c2ecf20Sopenharmony_ci		goto fail4;
11608c2ecf20Sopenharmony_ci	}
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	err = request_threaded_irq(client->irq, NULL,
11638c2ecf20Sopenharmony_ci				apds990x_irq,
11648c2ecf20Sopenharmony_ci				IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW |
11658c2ecf20Sopenharmony_ci				IRQF_ONESHOT,
11668c2ecf20Sopenharmony_ci				"apds990x", chip);
11678c2ecf20Sopenharmony_ci	if (err) {
11688c2ecf20Sopenharmony_ci		dev_err(&client->dev, "could not get IRQ %d\n",
11698c2ecf20Sopenharmony_ci			client->irq);
11708c2ecf20Sopenharmony_ci		goto fail5;
11718c2ecf20Sopenharmony_ci	}
11728c2ecf20Sopenharmony_ci	return err;
11738c2ecf20Sopenharmony_cifail5:
11748c2ecf20Sopenharmony_ci	sysfs_remove_group(&chip->client->dev.kobj,
11758c2ecf20Sopenharmony_ci			&apds990x_attribute_group[0]);
11768c2ecf20Sopenharmony_cifail4:
11778c2ecf20Sopenharmony_ci	if (chip->pdata && chip->pdata->release_resources)
11788c2ecf20Sopenharmony_ci		chip->pdata->release_resources();
11798c2ecf20Sopenharmony_cifail3:
11808c2ecf20Sopenharmony_ci	regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs);
11818c2ecf20Sopenharmony_cifail2:
11828c2ecf20Sopenharmony_ci	regulator_bulk_free(ARRAY_SIZE(chip->regs), chip->regs);
11838c2ecf20Sopenharmony_cifail1:
11848c2ecf20Sopenharmony_ci	kfree(chip);
11858c2ecf20Sopenharmony_ci	return err;
11868c2ecf20Sopenharmony_ci}
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_cistatic int apds990x_remove(struct i2c_client *client)
11898c2ecf20Sopenharmony_ci{
11908c2ecf20Sopenharmony_ci	struct apds990x_chip *chip = i2c_get_clientdata(client);
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	free_irq(client->irq, chip);
11938c2ecf20Sopenharmony_ci	sysfs_remove_group(&chip->client->dev.kobj,
11948c2ecf20Sopenharmony_ci			apds990x_attribute_group);
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci	if (chip->pdata && chip->pdata->release_resources)
11978c2ecf20Sopenharmony_ci		chip->pdata->release_resources();
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	if (!pm_runtime_suspended(&client->dev))
12008c2ecf20Sopenharmony_ci		apds990x_chip_off(chip);
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	pm_runtime_disable(&client->dev);
12038c2ecf20Sopenharmony_ci	pm_runtime_set_suspended(&client->dev);
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	regulator_bulk_free(ARRAY_SIZE(chip->regs), chip->regs);
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci	kfree(chip);
12088c2ecf20Sopenharmony_ci	return 0;
12098c2ecf20Sopenharmony_ci}
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
12128c2ecf20Sopenharmony_cistatic int apds990x_suspend(struct device *dev)
12138c2ecf20Sopenharmony_ci{
12148c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
12158c2ecf20Sopenharmony_ci	struct apds990x_chip *chip = i2c_get_clientdata(client);
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci	apds990x_chip_off(chip);
12188c2ecf20Sopenharmony_ci	return 0;
12198c2ecf20Sopenharmony_ci}
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_cistatic int apds990x_resume(struct device *dev)
12228c2ecf20Sopenharmony_ci{
12238c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
12248c2ecf20Sopenharmony_ci	struct apds990x_chip *chip = i2c_get_clientdata(client);
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	/*
12278c2ecf20Sopenharmony_ci	 * If we were enabled at suspend time, it is expected
12288c2ecf20Sopenharmony_ci	 * everything works nice and smoothly. Chip_on is enough
12298c2ecf20Sopenharmony_ci	 */
12308c2ecf20Sopenharmony_ci	apds990x_chip_on(chip);
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	return 0;
12338c2ecf20Sopenharmony_ci}
12348c2ecf20Sopenharmony_ci#endif
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
12378c2ecf20Sopenharmony_cistatic int apds990x_runtime_suspend(struct device *dev)
12388c2ecf20Sopenharmony_ci{
12398c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
12408c2ecf20Sopenharmony_ci	struct apds990x_chip *chip = i2c_get_clientdata(client);
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	apds990x_chip_off(chip);
12438c2ecf20Sopenharmony_ci	return 0;
12448c2ecf20Sopenharmony_ci}
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_cistatic int apds990x_runtime_resume(struct device *dev)
12478c2ecf20Sopenharmony_ci{
12488c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
12498c2ecf20Sopenharmony_ci	struct apds990x_chip *chip = i2c_get_clientdata(client);
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	apds990x_chip_on(chip);
12528c2ecf20Sopenharmony_ci	return 0;
12538c2ecf20Sopenharmony_ci}
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci#endif
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_cistatic const struct i2c_device_id apds990x_id[] = {
12588c2ecf20Sopenharmony_ci	{"apds990x", 0 },
12598c2ecf20Sopenharmony_ci	{}
12608c2ecf20Sopenharmony_ci};
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, apds990x_id);
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_cistatic const struct dev_pm_ops apds990x_pm_ops = {
12658c2ecf20Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(apds990x_suspend, apds990x_resume)
12668c2ecf20Sopenharmony_ci	SET_RUNTIME_PM_OPS(apds990x_runtime_suspend,
12678c2ecf20Sopenharmony_ci			apds990x_runtime_resume,
12688c2ecf20Sopenharmony_ci			NULL)
12698c2ecf20Sopenharmony_ci};
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_cistatic struct i2c_driver apds990x_driver = {
12728c2ecf20Sopenharmony_ci	.driver	 = {
12738c2ecf20Sopenharmony_ci		.name	= "apds990x",
12748c2ecf20Sopenharmony_ci		.pm	= &apds990x_pm_ops,
12758c2ecf20Sopenharmony_ci	},
12768c2ecf20Sopenharmony_ci	.probe	  = apds990x_probe,
12778c2ecf20Sopenharmony_ci	.remove	  = apds990x_remove,
12788c2ecf20Sopenharmony_ci	.id_table = apds990x_id,
12798c2ecf20Sopenharmony_ci};
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_cimodule_i2c_driver(apds990x_driver);
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("APDS990X combined ALS and proximity sensor");
12848c2ecf20Sopenharmony_ciMODULE_AUTHOR("Samu Onkalo, Nokia Corporation");
12858c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
1286