18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * drivers/iio/light/tsl2563.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Written by Timo O. Karjalainen <timo.o.karjalainen@nokia.com>
88c2ecf20Sopenharmony_ci * Contact: Amit Kucheria <amit.kucheria@verdurent.com>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Converted to IIO driver
118c2ecf20Sopenharmony_ci * Amit Kucheria <amit.kucheria@verdurent.com>
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/i2c.h>
168c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
178c2ecf20Sopenharmony_ci#include <linux/irq.h>
188c2ecf20Sopenharmony_ci#include <linux/sched.h>
198c2ecf20Sopenharmony_ci#include <linux/mutex.h>
208c2ecf20Sopenharmony_ci#include <linux/delay.h>
218c2ecf20Sopenharmony_ci#include <linux/pm.h>
228c2ecf20Sopenharmony_ci#include <linux/err.h>
238c2ecf20Sopenharmony_ci#include <linux/slab.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <linux/iio/iio.h>
268c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h>
278c2ecf20Sopenharmony_ci#include <linux/iio/events.h>
288c2ecf20Sopenharmony_ci#include <linux/platform_data/tsl2563.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/* Use this many bits for fraction part. */
318c2ecf20Sopenharmony_ci#define ADC_FRAC_BITS		14
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/* Given number of 1/10000's in ADC_FRAC_BITS precision. */
348c2ecf20Sopenharmony_ci#define FRAC10K(f)		(((f) * (1L << (ADC_FRAC_BITS))) / (10000))
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/* Bits used for fraction in calibration coefficients.*/
378c2ecf20Sopenharmony_ci#define CALIB_FRAC_BITS		10
388c2ecf20Sopenharmony_ci/* 0.5 in CALIB_FRAC_BITS precision */
398c2ecf20Sopenharmony_ci#define CALIB_FRAC_HALF		(1 << (CALIB_FRAC_BITS - 1))
408c2ecf20Sopenharmony_ci/* Make a fraction from a number n that was multiplied with b. */
418c2ecf20Sopenharmony_ci#define CALIB_FRAC(n, b)	(((n) << CALIB_FRAC_BITS) / (b))
428c2ecf20Sopenharmony_ci/* Decimal 10^(digits in sysfs presentation) */
438c2ecf20Sopenharmony_ci#define CALIB_BASE_SYSFS	1000
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define TSL2563_CMD		0x80
468c2ecf20Sopenharmony_ci#define TSL2563_CLEARINT	0x40
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define TSL2563_REG_CTRL	0x00
498c2ecf20Sopenharmony_ci#define TSL2563_REG_TIMING	0x01
508c2ecf20Sopenharmony_ci#define TSL2563_REG_LOWLOW	0x02 /* data0 low threshold, 2 bytes */
518c2ecf20Sopenharmony_ci#define TSL2563_REG_LOWHIGH	0x03
528c2ecf20Sopenharmony_ci#define TSL2563_REG_HIGHLOW	0x04 /* data0 high threshold, 2 bytes */
538c2ecf20Sopenharmony_ci#define TSL2563_REG_HIGHHIGH	0x05
548c2ecf20Sopenharmony_ci#define TSL2563_REG_INT		0x06
558c2ecf20Sopenharmony_ci#define TSL2563_REG_ID		0x0a
568c2ecf20Sopenharmony_ci#define TSL2563_REG_DATA0LOW	0x0c /* broadband sensor value, 2 bytes */
578c2ecf20Sopenharmony_ci#define TSL2563_REG_DATA0HIGH	0x0d
588c2ecf20Sopenharmony_ci#define TSL2563_REG_DATA1LOW	0x0e /* infrared sensor value, 2 bytes */
598c2ecf20Sopenharmony_ci#define TSL2563_REG_DATA1HIGH	0x0f
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#define TSL2563_CMD_POWER_ON	0x03
628c2ecf20Sopenharmony_ci#define TSL2563_CMD_POWER_OFF	0x00
638c2ecf20Sopenharmony_ci#define TSL2563_CTRL_POWER_MASK	0x03
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#define TSL2563_TIMING_13MS	0x00
668c2ecf20Sopenharmony_ci#define TSL2563_TIMING_100MS	0x01
678c2ecf20Sopenharmony_ci#define TSL2563_TIMING_400MS	0x02
688c2ecf20Sopenharmony_ci#define TSL2563_TIMING_MASK	0x03
698c2ecf20Sopenharmony_ci#define TSL2563_TIMING_GAIN16	0x10
708c2ecf20Sopenharmony_ci#define TSL2563_TIMING_GAIN1	0x00
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci#define TSL2563_INT_DISABLED	0x00
738c2ecf20Sopenharmony_ci#define TSL2563_INT_LEVEL	0x10
748c2ecf20Sopenharmony_ci#define TSL2563_INT_PERSIST(n)	((n) & 0x0F)
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistruct tsl2563_gainlevel_coeff {
778c2ecf20Sopenharmony_ci	u8 gaintime;
788c2ecf20Sopenharmony_ci	u16 min;
798c2ecf20Sopenharmony_ci	u16 max;
808c2ecf20Sopenharmony_ci};
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic const struct tsl2563_gainlevel_coeff tsl2563_gainlevel_table[] = {
838c2ecf20Sopenharmony_ci	{
848c2ecf20Sopenharmony_ci		.gaintime	= TSL2563_TIMING_400MS | TSL2563_TIMING_GAIN16,
858c2ecf20Sopenharmony_ci		.min		= 0,
868c2ecf20Sopenharmony_ci		.max		= 65534,
878c2ecf20Sopenharmony_ci	}, {
888c2ecf20Sopenharmony_ci		.gaintime	= TSL2563_TIMING_400MS | TSL2563_TIMING_GAIN1,
898c2ecf20Sopenharmony_ci		.min		= 2048,
908c2ecf20Sopenharmony_ci		.max		= 65534,
918c2ecf20Sopenharmony_ci	}, {
928c2ecf20Sopenharmony_ci		.gaintime	= TSL2563_TIMING_100MS | TSL2563_TIMING_GAIN1,
938c2ecf20Sopenharmony_ci		.min		= 4095,
948c2ecf20Sopenharmony_ci		.max		= 37177,
958c2ecf20Sopenharmony_ci	}, {
968c2ecf20Sopenharmony_ci		.gaintime	= TSL2563_TIMING_13MS | TSL2563_TIMING_GAIN1,
978c2ecf20Sopenharmony_ci		.min		= 3000,
988c2ecf20Sopenharmony_ci		.max		= 65535,
998c2ecf20Sopenharmony_ci	},
1008c2ecf20Sopenharmony_ci};
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistruct tsl2563_chip {
1038c2ecf20Sopenharmony_ci	struct mutex		lock;
1048c2ecf20Sopenharmony_ci	struct i2c_client	*client;
1058c2ecf20Sopenharmony_ci	struct delayed_work	poweroff_work;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	/* Remember state for suspend and resume functions */
1088c2ecf20Sopenharmony_ci	bool suspended;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	struct tsl2563_gainlevel_coeff const *gainlevel;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	u16			low_thres;
1138c2ecf20Sopenharmony_ci	u16			high_thres;
1148c2ecf20Sopenharmony_ci	u8			intr;
1158c2ecf20Sopenharmony_ci	bool			int_enabled;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	/* Calibration coefficients */
1188c2ecf20Sopenharmony_ci	u32			calib0;
1198c2ecf20Sopenharmony_ci	u32			calib1;
1208c2ecf20Sopenharmony_ci	int			cover_comp_gain;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	/* Cache current values, to be returned while suspended */
1238c2ecf20Sopenharmony_ci	u32			data0;
1248c2ecf20Sopenharmony_ci	u32			data1;
1258c2ecf20Sopenharmony_ci};
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic int tsl2563_set_power(struct tsl2563_chip *chip, int on)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	struct i2c_client *client = chip->client;
1308c2ecf20Sopenharmony_ci	u8 cmd;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	cmd = on ? TSL2563_CMD_POWER_ON : TSL2563_CMD_POWER_OFF;
1338c2ecf20Sopenharmony_ci	return i2c_smbus_write_byte_data(client,
1348c2ecf20Sopenharmony_ci					 TSL2563_CMD | TSL2563_REG_CTRL, cmd);
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci/*
1388c2ecf20Sopenharmony_ci * Return value is 0 for off, 1 for on, or a negative error
1398c2ecf20Sopenharmony_ci * code if reading failed.
1408c2ecf20Sopenharmony_ci */
1418c2ecf20Sopenharmony_cistatic int tsl2563_get_power(struct tsl2563_chip *chip)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	struct i2c_client *client = chip->client;
1448c2ecf20Sopenharmony_ci	int ret;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	ret = i2c_smbus_read_byte_data(client, TSL2563_CMD | TSL2563_REG_CTRL);
1478c2ecf20Sopenharmony_ci	if (ret < 0)
1488c2ecf20Sopenharmony_ci		return ret;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	return (ret & TSL2563_CTRL_POWER_MASK) == TSL2563_CMD_POWER_ON;
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic int tsl2563_configure(struct tsl2563_chip *chip)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	int ret;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	ret = i2c_smbus_write_byte_data(chip->client,
1588c2ecf20Sopenharmony_ci			TSL2563_CMD | TSL2563_REG_TIMING,
1598c2ecf20Sopenharmony_ci			chip->gainlevel->gaintime);
1608c2ecf20Sopenharmony_ci	if (ret)
1618c2ecf20Sopenharmony_ci		goto error_ret;
1628c2ecf20Sopenharmony_ci	ret = i2c_smbus_write_byte_data(chip->client,
1638c2ecf20Sopenharmony_ci			TSL2563_CMD | TSL2563_REG_HIGHLOW,
1648c2ecf20Sopenharmony_ci			chip->high_thres & 0xFF);
1658c2ecf20Sopenharmony_ci	if (ret)
1668c2ecf20Sopenharmony_ci		goto error_ret;
1678c2ecf20Sopenharmony_ci	ret = i2c_smbus_write_byte_data(chip->client,
1688c2ecf20Sopenharmony_ci			TSL2563_CMD | TSL2563_REG_HIGHHIGH,
1698c2ecf20Sopenharmony_ci			(chip->high_thres >> 8) & 0xFF);
1708c2ecf20Sopenharmony_ci	if (ret)
1718c2ecf20Sopenharmony_ci		goto error_ret;
1728c2ecf20Sopenharmony_ci	ret = i2c_smbus_write_byte_data(chip->client,
1738c2ecf20Sopenharmony_ci			TSL2563_CMD | TSL2563_REG_LOWLOW,
1748c2ecf20Sopenharmony_ci			chip->low_thres & 0xFF);
1758c2ecf20Sopenharmony_ci	if (ret)
1768c2ecf20Sopenharmony_ci		goto error_ret;
1778c2ecf20Sopenharmony_ci	ret = i2c_smbus_write_byte_data(chip->client,
1788c2ecf20Sopenharmony_ci			TSL2563_CMD | TSL2563_REG_LOWHIGH,
1798c2ecf20Sopenharmony_ci			(chip->low_thres >> 8) & 0xFF);
1808c2ecf20Sopenharmony_ci/*
1818c2ecf20Sopenharmony_ci * Interrupt register is automatically written anyway if it is relevant
1828c2ecf20Sopenharmony_ci * so is not here.
1838c2ecf20Sopenharmony_ci */
1848c2ecf20Sopenharmony_cierror_ret:
1858c2ecf20Sopenharmony_ci	return ret;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic void tsl2563_poweroff_work(struct work_struct *work)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	struct tsl2563_chip *chip =
1918c2ecf20Sopenharmony_ci		container_of(work, struct tsl2563_chip, poweroff_work.work);
1928c2ecf20Sopenharmony_ci	tsl2563_set_power(chip, 0);
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic int tsl2563_detect(struct tsl2563_chip *chip)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	int ret;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	ret = tsl2563_set_power(chip, 1);
2008c2ecf20Sopenharmony_ci	if (ret)
2018c2ecf20Sopenharmony_ci		return ret;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	ret = tsl2563_get_power(chip);
2048c2ecf20Sopenharmony_ci	if (ret < 0)
2058c2ecf20Sopenharmony_ci		return ret;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	return ret ? 0 : -ENODEV;
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic int tsl2563_read_id(struct tsl2563_chip *chip, u8 *id)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	struct i2c_client *client = chip->client;
2138c2ecf20Sopenharmony_ci	int ret;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	ret = i2c_smbus_read_byte_data(client, TSL2563_CMD | TSL2563_REG_ID);
2168c2ecf20Sopenharmony_ci	if (ret < 0)
2178c2ecf20Sopenharmony_ci		return ret;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	*id = ret;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	return 0;
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci/*
2258c2ecf20Sopenharmony_ci * "Normalized" ADC value is one obtained with 400ms of integration time and
2268c2ecf20Sopenharmony_ci * 16x gain. This function returns the number of bits of shift needed to
2278c2ecf20Sopenharmony_ci * convert between normalized values and HW values obtained using given
2288c2ecf20Sopenharmony_ci * timing and gain settings.
2298c2ecf20Sopenharmony_ci */
2308c2ecf20Sopenharmony_cistatic int tsl2563_adc_shiftbits(u8 timing)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	int shift = 0;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	switch (timing & TSL2563_TIMING_MASK) {
2358c2ecf20Sopenharmony_ci	case TSL2563_TIMING_13MS:
2368c2ecf20Sopenharmony_ci		shift += 5;
2378c2ecf20Sopenharmony_ci		break;
2388c2ecf20Sopenharmony_ci	case TSL2563_TIMING_100MS:
2398c2ecf20Sopenharmony_ci		shift += 2;
2408c2ecf20Sopenharmony_ci		break;
2418c2ecf20Sopenharmony_ci	case TSL2563_TIMING_400MS:
2428c2ecf20Sopenharmony_ci		/* no-op */
2438c2ecf20Sopenharmony_ci		break;
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	if (!(timing & TSL2563_TIMING_GAIN16))
2478c2ecf20Sopenharmony_ci		shift += 4;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	return shift;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci/* Convert a HW ADC value to normalized scale. */
2538c2ecf20Sopenharmony_cistatic u32 tsl2563_normalize_adc(u16 adc, u8 timing)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	return adc << tsl2563_adc_shiftbits(timing);
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic void tsl2563_wait_adc(struct tsl2563_chip *chip)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	unsigned int delay;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	switch (chip->gainlevel->gaintime & TSL2563_TIMING_MASK) {
2638c2ecf20Sopenharmony_ci	case TSL2563_TIMING_13MS:
2648c2ecf20Sopenharmony_ci		delay = 14;
2658c2ecf20Sopenharmony_ci		break;
2668c2ecf20Sopenharmony_ci	case TSL2563_TIMING_100MS:
2678c2ecf20Sopenharmony_ci		delay = 101;
2688c2ecf20Sopenharmony_ci		break;
2698c2ecf20Sopenharmony_ci	default:
2708c2ecf20Sopenharmony_ci		delay = 402;
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci	/*
2738c2ecf20Sopenharmony_ci	 * TODO: Make sure that we wait at least required delay but why we
2748c2ecf20Sopenharmony_ci	 * have to extend it one tick more?
2758c2ecf20Sopenharmony_ci	 */
2768c2ecf20Sopenharmony_ci	schedule_timeout_interruptible(msecs_to_jiffies(delay) + 2);
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic int tsl2563_adjust_gainlevel(struct tsl2563_chip *chip, u16 adc)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	struct i2c_client *client = chip->client;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if (adc > chip->gainlevel->max || adc < chip->gainlevel->min) {
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci		(adc > chip->gainlevel->max) ?
2868c2ecf20Sopenharmony_ci			chip->gainlevel++ : chip->gainlevel--;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci		i2c_smbus_write_byte_data(client,
2898c2ecf20Sopenharmony_ci					  TSL2563_CMD | TSL2563_REG_TIMING,
2908c2ecf20Sopenharmony_ci					  chip->gainlevel->gaintime);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci		tsl2563_wait_adc(chip);
2938c2ecf20Sopenharmony_ci		tsl2563_wait_adc(chip);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci		return 1;
2968c2ecf20Sopenharmony_ci	} else
2978c2ecf20Sopenharmony_ci		return 0;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic int tsl2563_get_adc(struct tsl2563_chip *chip)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	struct i2c_client *client = chip->client;
3038c2ecf20Sopenharmony_ci	u16 adc0, adc1;
3048c2ecf20Sopenharmony_ci	int retry = 1;
3058c2ecf20Sopenharmony_ci	int ret = 0;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	if (chip->suspended)
3088c2ecf20Sopenharmony_ci		goto out;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	if (!chip->int_enabled) {
3118c2ecf20Sopenharmony_ci		cancel_delayed_work(&chip->poweroff_work);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci		if (!tsl2563_get_power(chip)) {
3148c2ecf20Sopenharmony_ci			ret = tsl2563_set_power(chip, 1);
3158c2ecf20Sopenharmony_ci			if (ret)
3168c2ecf20Sopenharmony_ci				goto out;
3178c2ecf20Sopenharmony_ci			ret = tsl2563_configure(chip);
3188c2ecf20Sopenharmony_ci			if (ret)
3198c2ecf20Sopenharmony_ci				goto out;
3208c2ecf20Sopenharmony_ci			tsl2563_wait_adc(chip);
3218c2ecf20Sopenharmony_ci		}
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	while (retry) {
3258c2ecf20Sopenharmony_ci		ret = i2c_smbus_read_word_data(client,
3268c2ecf20Sopenharmony_ci				TSL2563_CMD | TSL2563_REG_DATA0LOW);
3278c2ecf20Sopenharmony_ci		if (ret < 0)
3288c2ecf20Sopenharmony_ci			goto out;
3298c2ecf20Sopenharmony_ci		adc0 = ret;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci		ret = i2c_smbus_read_word_data(client,
3328c2ecf20Sopenharmony_ci				TSL2563_CMD | TSL2563_REG_DATA1LOW);
3338c2ecf20Sopenharmony_ci		if (ret < 0)
3348c2ecf20Sopenharmony_ci			goto out;
3358c2ecf20Sopenharmony_ci		adc1 = ret;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci		retry = tsl2563_adjust_gainlevel(chip, adc0);
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	chip->data0 = tsl2563_normalize_adc(adc0, chip->gainlevel->gaintime);
3418c2ecf20Sopenharmony_ci	chip->data1 = tsl2563_normalize_adc(adc1, chip->gainlevel->gaintime);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	if (!chip->int_enabled)
3448c2ecf20Sopenharmony_ci		schedule_delayed_work(&chip->poweroff_work, 5 * HZ);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	ret = 0;
3478c2ecf20Sopenharmony_ciout:
3488c2ecf20Sopenharmony_ci	return ret;
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic inline int tsl2563_calib_to_sysfs(u32 calib)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	return (int) (((calib * CALIB_BASE_SYSFS) +
3548c2ecf20Sopenharmony_ci		       CALIB_FRAC_HALF) >> CALIB_FRAC_BITS);
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic inline u32 tsl2563_calib_from_sysfs(int value)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	return (((u32) value) << CALIB_FRAC_BITS) / CALIB_BASE_SYSFS;
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci/*
3638c2ecf20Sopenharmony_ci * Conversions between lux and ADC values.
3648c2ecf20Sopenharmony_ci *
3658c2ecf20Sopenharmony_ci * The basic formula is lux = c0 * adc0 - c1 * adc1, where c0 and c1 are
3668c2ecf20Sopenharmony_ci * appropriate constants. Different constants are needed for different
3678c2ecf20Sopenharmony_ci * kinds of light, determined by the ratio adc1/adc0 (basically the ratio
3688c2ecf20Sopenharmony_ci * of the intensities in infrared and visible wavelengths). lux_table below
3698c2ecf20Sopenharmony_ci * lists the upper threshold of the adc1/adc0 ratio and the corresponding
3708c2ecf20Sopenharmony_ci * constants.
3718c2ecf20Sopenharmony_ci */
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistruct tsl2563_lux_coeff {
3748c2ecf20Sopenharmony_ci	unsigned long ch_ratio;
3758c2ecf20Sopenharmony_ci	unsigned long ch0_coeff;
3768c2ecf20Sopenharmony_ci	unsigned long ch1_coeff;
3778c2ecf20Sopenharmony_ci};
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic const struct tsl2563_lux_coeff lux_table[] = {
3808c2ecf20Sopenharmony_ci	{
3818c2ecf20Sopenharmony_ci		.ch_ratio	= FRAC10K(1300),
3828c2ecf20Sopenharmony_ci		.ch0_coeff	= FRAC10K(315),
3838c2ecf20Sopenharmony_ci		.ch1_coeff	= FRAC10K(262),
3848c2ecf20Sopenharmony_ci	}, {
3858c2ecf20Sopenharmony_ci		.ch_ratio	= FRAC10K(2600),
3868c2ecf20Sopenharmony_ci		.ch0_coeff	= FRAC10K(337),
3878c2ecf20Sopenharmony_ci		.ch1_coeff	= FRAC10K(430),
3888c2ecf20Sopenharmony_ci	}, {
3898c2ecf20Sopenharmony_ci		.ch_ratio	= FRAC10K(3900),
3908c2ecf20Sopenharmony_ci		.ch0_coeff	= FRAC10K(363),
3918c2ecf20Sopenharmony_ci		.ch1_coeff	= FRAC10K(529),
3928c2ecf20Sopenharmony_ci	}, {
3938c2ecf20Sopenharmony_ci		.ch_ratio	= FRAC10K(5200),
3948c2ecf20Sopenharmony_ci		.ch0_coeff	= FRAC10K(392),
3958c2ecf20Sopenharmony_ci		.ch1_coeff	= FRAC10K(605),
3968c2ecf20Sopenharmony_ci	}, {
3978c2ecf20Sopenharmony_ci		.ch_ratio	= FRAC10K(6500),
3988c2ecf20Sopenharmony_ci		.ch0_coeff	= FRAC10K(229),
3998c2ecf20Sopenharmony_ci		.ch1_coeff	= FRAC10K(291),
4008c2ecf20Sopenharmony_ci	}, {
4018c2ecf20Sopenharmony_ci		.ch_ratio	= FRAC10K(8000),
4028c2ecf20Sopenharmony_ci		.ch0_coeff	= FRAC10K(157),
4038c2ecf20Sopenharmony_ci		.ch1_coeff	= FRAC10K(180),
4048c2ecf20Sopenharmony_ci	}, {
4058c2ecf20Sopenharmony_ci		.ch_ratio	= FRAC10K(13000),
4068c2ecf20Sopenharmony_ci		.ch0_coeff	= FRAC10K(34),
4078c2ecf20Sopenharmony_ci		.ch1_coeff	= FRAC10K(26),
4088c2ecf20Sopenharmony_ci	}, {
4098c2ecf20Sopenharmony_ci		.ch_ratio	= ULONG_MAX,
4108c2ecf20Sopenharmony_ci		.ch0_coeff	= 0,
4118c2ecf20Sopenharmony_ci		.ch1_coeff	= 0,
4128c2ecf20Sopenharmony_ci	},
4138c2ecf20Sopenharmony_ci};
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci/* Convert normalized, scaled ADC values to lux. */
4168c2ecf20Sopenharmony_cistatic unsigned int tsl2563_adc_to_lux(u32 adc0, u32 adc1)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	const struct tsl2563_lux_coeff *lp = lux_table;
4198c2ecf20Sopenharmony_ci	unsigned long ratio, lux, ch0 = adc0, ch1 = adc1;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	ratio = ch0 ? ((ch1 << ADC_FRAC_BITS) / ch0) : ULONG_MAX;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	while (lp->ch_ratio < ratio)
4248c2ecf20Sopenharmony_ci		lp++;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	lux = ch0 * lp->ch0_coeff - ch1 * lp->ch1_coeff;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	return (unsigned int) (lux >> ADC_FRAC_BITS);
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci/* Apply calibration coefficient to ADC count. */
4328c2ecf20Sopenharmony_cistatic u32 tsl2563_calib_adc(u32 adc, u32 calib)
4338c2ecf20Sopenharmony_ci{
4348c2ecf20Sopenharmony_ci	unsigned long scaled = adc;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	scaled *= calib;
4378c2ecf20Sopenharmony_ci	scaled >>= CALIB_FRAC_BITS;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	return (u32) scaled;
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic int tsl2563_write_raw(struct iio_dev *indio_dev,
4438c2ecf20Sopenharmony_ci			       struct iio_chan_spec const *chan,
4448c2ecf20Sopenharmony_ci			       int val,
4458c2ecf20Sopenharmony_ci			       int val2,
4468c2ecf20Sopenharmony_ci			       long mask)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	struct tsl2563_chip *chip = iio_priv(indio_dev);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	if (mask != IIO_CHAN_INFO_CALIBSCALE)
4518c2ecf20Sopenharmony_ci		return -EINVAL;
4528c2ecf20Sopenharmony_ci	if (chan->channel2 == IIO_MOD_LIGHT_BOTH)
4538c2ecf20Sopenharmony_ci		chip->calib0 = tsl2563_calib_from_sysfs(val);
4548c2ecf20Sopenharmony_ci	else if (chan->channel2 == IIO_MOD_LIGHT_IR)
4558c2ecf20Sopenharmony_ci		chip->calib1 = tsl2563_calib_from_sysfs(val);
4568c2ecf20Sopenharmony_ci	else
4578c2ecf20Sopenharmony_ci		return -EINVAL;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	return 0;
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_cistatic int tsl2563_read_raw(struct iio_dev *indio_dev,
4638c2ecf20Sopenharmony_ci			    struct iio_chan_spec const *chan,
4648c2ecf20Sopenharmony_ci			    int *val,
4658c2ecf20Sopenharmony_ci			    int *val2,
4668c2ecf20Sopenharmony_ci			    long mask)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	int ret = -EINVAL;
4698c2ecf20Sopenharmony_ci	u32 calib0, calib1;
4708c2ecf20Sopenharmony_ci	struct tsl2563_chip *chip = iio_priv(indio_dev);
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	mutex_lock(&chip->lock);
4738c2ecf20Sopenharmony_ci	switch (mask) {
4748c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
4758c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_PROCESSED:
4768c2ecf20Sopenharmony_ci		switch (chan->type) {
4778c2ecf20Sopenharmony_ci		case IIO_LIGHT:
4788c2ecf20Sopenharmony_ci			ret = tsl2563_get_adc(chip);
4798c2ecf20Sopenharmony_ci			if (ret)
4808c2ecf20Sopenharmony_ci				goto error_ret;
4818c2ecf20Sopenharmony_ci			calib0 = tsl2563_calib_adc(chip->data0, chip->calib0) *
4828c2ecf20Sopenharmony_ci				chip->cover_comp_gain;
4838c2ecf20Sopenharmony_ci			calib1 = tsl2563_calib_adc(chip->data1, chip->calib1) *
4848c2ecf20Sopenharmony_ci				chip->cover_comp_gain;
4858c2ecf20Sopenharmony_ci			*val = tsl2563_adc_to_lux(calib0, calib1);
4868c2ecf20Sopenharmony_ci			ret = IIO_VAL_INT;
4878c2ecf20Sopenharmony_ci			break;
4888c2ecf20Sopenharmony_ci		case IIO_INTENSITY:
4898c2ecf20Sopenharmony_ci			ret = tsl2563_get_adc(chip);
4908c2ecf20Sopenharmony_ci			if (ret)
4918c2ecf20Sopenharmony_ci				goto error_ret;
4928c2ecf20Sopenharmony_ci			if (chan->channel2 == IIO_MOD_LIGHT_BOTH)
4938c2ecf20Sopenharmony_ci				*val = chip->data0;
4948c2ecf20Sopenharmony_ci			else
4958c2ecf20Sopenharmony_ci				*val = chip->data1;
4968c2ecf20Sopenharmony_ci			ret = IIO_VAL_INT;
4978c2ecf20Sopenharmony_ci			break;
4988c2ecf20Sopenharmony_ci		default:
4998c2ecf20Sopenharmony_ci			break;
5008c2ecf20Sopenharmony_ci		}
5018c2ecf20Sopenharmony_ci		break;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_CALIBSCALE:
5048c2ecf20Sopenharmony_ci		if (chan->channel2 == IIO_MOD_LIGHT_BOTH)
5058c2ecf20Sopenharmony_ci			*val = tsl2563_calib_to_sysfs(chip->calib0);
5068c2ecf20Sopenharmony_ci		else
5078c2ecf20Sopenharmony_ci			*val = tsl2563_calib_to_sysfs(chip->calib1);
5088c2ecf20Sopenharmony_ci		ret = IIO_VAL_INT;
5098c2ecf20Sopenharmony_ci		break;
5108c2ecf20Sopenharmony_ci	default:
5118c2ecf20Sopenharmony_ci		ret = -EINVAL;
5128c2ecf20Sopenharmony_ci		goto error_ret;
5138c2ecf20Sopenharmony_ci	}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_cierror_ret:
5168c2ecf20Sopenharmony_ci	mutex_unlock(&chip->lock);
5178c2ecf20Sopenharmony_ci	return ret;
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_cistatic const struct iio_event_spec tsl2563_events[] = {
5218c2ecf20Sopenharmony_ci	{
5228c2ecf20Sopenharmony_ci		.type = IIO_EV_TYPE_THRESH,
5238c2ecf20Sopenharmony_ci		.dir = IIO_EV_DIR_RISING,
5248c2ecf20Sopenharmony_ci		.mask_separate = BIT(IIO_EV_INFO_VALUE) |
5258c2ecf20Sopenharmony_ci				BIT(IIO_EV_INFO_ENABLE),
5268c2ecf20Sopenharmony_ci	}, {
5278c2ecf20Sopenharmony_ci		.type = IIO_EV_TYPE_THRESH,
5288c2ecf20Sopenharmony_ci		.dir = IIO_EV_DIR_FALLING,
5298c2ecf20Sopenharmony_ci		.mask_separate = BIT(IIO_EV_INFO_VALUE) |
5308c2ecf20Sopenharmony_ci				BIT(IIO_EV_INFO_ENABLE),
5318c2ecf20Sopenharmony_ci	},
5328c2ecf20Sopenharmony_ci};
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_cistatic const struct iio_chan_spec tsl2563_channels[] = {
5358c2ecf20Sopenharmony_ci	{
5368c2ecf20Sopenharmony_ci		.type = IIO_LIGHT,
5378c2ecf20Sopenharmony_ci		.indexed = 1,
5388c2ecf20Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
5398c2ecf20Sopenharmony_ci		.channel = 0,
5408c2ecf20Sopenharmony_ci	}, {
5418c2ecf20Sopenharmony_ci		.type = IIO_INTENSITY,
5428c2ecf20Sopenharmony_ci		.modified = 1,
5438c2ecf20Sopenharmony_ci		.channel2 = IIO_MOD_LIGHT_BOTH,
5448c2ecf20Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
5458c2ecf20Sopenharmony_ci		BIT(IIO_CHAN_INFO_CALIBSCALE),
5468c2ecf20Sopenharmony_ci		.event_spec = tsl2563_events,
5478c2ecf20Sopenharmony_ci		.num_event_specs = ARRAY_SIZE(tsl2563_events),
5488c2ecf20Sopenharmony_ci	}, {
5498c2ecf20Sopenharmony_ci		.type = IIO_INTENSITY,
5508c2ecf20Sopenharmony_ci		.modified = 1,
5518c2ecf20Sopenharmony_ci		.channel2 = IIO_MOD_LIGHT_IR,
5528c2ecf20Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
5538c2ecf20Sopenharmony_ci		BIT(IIO_CHAN_INFO_CALIBSCALE),
5548c2ecf20Sopenharmony_ci	}
5558c2ecf20Sopenharmony_ci};
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_cistatic int tsl2563_read_thresh(struct iio_dev *indio_dev,
5588c2ecf20Sopenharmony_ci	const struct iio_chan_spec *chan, enum iio_event_type type,
5598c2ecf20Sopenharmony_ci	enum iio_event_direction dir, enum iio_event_info info, int *val,
5608c2ecf20Sopenharmony_ci	int *val2)
5618c2ecf20Sopenharmony_ci{
5628c2ecf20Sopenharmony_ci	struct tsl2563_chip *chip = iio_priv(indio_dev);
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	switch (dir) {
5658c2ecf20Sopenharmony_ci	case IIO_EV_DIR_RISING:
5668c2ecf20Sopenharmony_ci		*val = chip->high_thres;
5678c2ecf20Sopenharmony_ci		break;
5688c2ecf20Sopenharmony_ci	case IIO_EV_DIR_FALLING:
5698c2ecf20Sopenharmony_ci		*val = chip->low_thres;
5708c2ecf20Sopenharmony_ci		break;
5718c2ecf20Sopenharmony_ci	default:
5728c2ecf20Sopenharmony_ci		return -EINVAL;
5738c2ecf20Sopenharmony_ci	}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	return IIO_VAL_INT;
5768c2ecf20Sopenharmony_ci}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_cistatic int tsl2563_write_thresh(struct iio_dev *indio_dev,
5798c2ecf20Sopenharmony_ci	const struct iio_chan_spec *chan, enum iio_event_type type,
5808c2ecf20Sopenharmony_ci	enum iio_event_direction dir, enum iio_event_info info, int val,
5818c2ecf20Sopenharmony_ci	int val2)
5828c2ecf20Sopenharmony_ci{
5838c2ecf20Sopenharmony_ci	struct tsl2563_chip *chip = iio_priv(indio_dev);
5848c2ecf20Sopenharmony_ci	int ret;
5858c2ecf20Sopenharmony_ci	u8 address;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	if (dir == IIO_EV_DIR_RISING)
5888c2ecf20Sopenharmony_ci		address = TSL2563_REG_HIGHLOW;
5898c2ecf20Sopenharmony_ci	else
5908c2ecf20Sopenharmony_ci		address = TSL2563_REG_LOWLOW;
5918c2ecf20Sopenharmony_ci	mutex_lock(&chip->lock);
5928c2ecf20Sopenharmony_ci	ret = i2c_smbus_write_byte_data(chip->client, TSL2563_CMD | address,
5938c2ecf20Sopenharmony_ci					val & 0xFF);
5948c2ecf20Sopenharmony_ci	if (ret)
5958c2ecf20Sopenharmony_ci		goto error_ret;
5968c2ecf20Sopenharmony_ci	ret = i2c_smbus_write_byte_data(chip->client,
5978c2ecf20Sopenharmony_ci					TSL2563_CMD | (address + 1),
5988c2ecf20Sopenharmony_ci					(val >> 8) & 0xFF);
5998c2ecf20Sopenharmony_ci	if (dir == IIO_EV_DIR_RISING)
6008c2ecf20Sopenharmony_ci		chip->high_thres = val;
6018c2ecf20Sopenharmony_ci	else
6028c2ecf20Sopenharmony_ci		chip->low_thres = val;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_cierror_ret:
6058c2ecf20Sopenharmony_ci	mutex_unlock(&chip->lock);
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	return ret;
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_cistatic irqreturn_t tsl2563_event_handler(int irq, void *private)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	struct iio_dev *dev_info = private;
6138c2ecf20Sopenharmony_ci	struct tsl2563_chip *chip = iio_priv(dev_info);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	iio_push_event(dev_info,
6168c2ecf20Sopenharmony_ci		       IIO_UNMOD_EVENT_CODE(IIO_INTENSITY,
6178c2ecf20Sopenharmony_ci					    0,
6188c2ecf20Sopenharmony_ci					    IIO_EV_TYPE_THRESH,
6198c2ecf20Sopenharmony_ci					    IIO_EV_DIR_EITHER),
6208c2ecf20Sopenharmony_ci		       iio_get_time_ns(dev_info));
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	/* clear the interrupt and push the event */
6238c2ecf20Sopenharmony_ci	i2c_smbus_write_byte(chip->client, TSL2563_CMD | TSL2563_CLEARINT);
6248c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
6258c2ecf20Sopenharmony_ci}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_cistatic int tsl2563_write_interrupt_config(struct iio_dev *indio_dev,
6288c2ecf20Sopenharmony_ci	const struct iio_chan_spec *chan, enum iio_event_type type,
6298c2ecf20Sopenharmony_ci	enum iio_event_direction dir, int state)
6308c2ecf20Sopenharmony_ci{
6318c2ecf20Sopenharmony_ci	struct tsl2563_chip *chip = iio_priv(indio_dev);
6328c2ecf20Sopenharmony_ci	int ret = 0;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	mutex_lock(&chip->lock);
6358c2ecf20Sopenharmony_ci	if (state && !(chip->intr & 0x30)) {
6368c2ecf20Sopenharmony_ci		chip->intr &= ~0x30;
6378c2ecf20Sopenharmony_ci		chip->intr |= 0x10;
6388c2ecf20Sopenharmony_ci		/* ensure the chip is actually on */
6398c2ecf20Sopenharmony_ci		cancel_delayed_work(&chip->poweroff_work);
6408c2ecf20Sopenharmony_ci		if (!tsl2563_get_power(chip)) {
6418c2ecf20Sopenharmony_ci			ret = tsl2563_set_power(chip, 1);
6428c2ecf20Sopenharmony_ci			if (ret)
6438c2ecf20Sopenharmony_ci				goto out;
6448c2ecf20Sopenharmony_ci			ret = tsl2563_configure(chip);
6458c2ecf20Sopenharmony_ci			if (ret)
6468c2ecf20Sopenharmony_ci				goto out;
6478c2ecf20Sopenharmony_ci		}
6488c2ecf20Sopenharmony_ci		ret = i2c_smbus_write_byte_data(chip->client,
6498c2ecf20Sopenharmony_ci						TSL2563_CMD | TSL2563_REG_INT,
6508c2ecf20Sopenharmony_ci						chip->intr);
6518c2ecf20Sopenharmony_ci		chip->int_enabled = true;
6528c2ecf20Sopenharmony_ci	}
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	if (!state && (chip->intr & 0x30)) {
6558c2ecf20Sopenharmony_ci		chip->intr &= ~0x30;
6568c2ecf20Sopenharmony_ci		ret = i2c_smbus_write_byte_data(chip->client,
6578c2ecf20Sopenharmony_ci						TSL2563_CMD | TSL2563_REG_INT,
6588c2ecf20Sopenharmony_ci						chip->intr);
6598c2ecf20Sopenharmony_ci		chip->int_enabled = false;
6608c2ecf20Sopenharmony_ci		/* now the interrupt is not enabled, we can go to sleep */
6618c2ecf20Sopenharmony_ci		schedule_delayed_work(&chip->poweroff_work, 5 * HZ);
6628c2ecf20Sopenharmony_ci	}
6638c2ecf20Sopenharmony_ciout:
6648c2ecf20Sopenharmony_ci	mutex_unlock(&chip->lock);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	return ret;
6678c2ecf20Sopenharmony_ci}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_cistatic int tsl2563_read_interrupt_config(struct iio_dev *indio_dev,
6708c2ecf20Sopenharmony_ci	const struct iio_chan_spec *chan, enum iio_event_type type,
6718c2ecf20Sopenharmony_ci	enum iio_event_direction dir)
6728c2ecf20Sopenharmony_ci{
6738c2ecf20Sopenharmony_ci	struct tsl2563_chip *chip = iio_priv(indio_dev);
6748c2ecf20Sopenharmony_ci	int ret;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	mutex_lock(&chip->lock);
6778c2ecf20Sopenharmony_ci	ret = i2c_smbus_read_byte_data(chip->client,
6788c2ecf20Sopenharmony_ci				       TSL2563_CMD | TSL2563_REG_INT);
6798c2ecf20Sopenharmony_ci	mutex_unlock(&chip->lock);
6808c2ecf20Sopenharmony_ci	if (ret < 0)
6818c2ecf20Sopenharmony_ci		return ret;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	return !!(ret & 0x30);
6848c2ecf20Sopenharmony_ci}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_cistatic const struct iio_info tsl2563_info_no_irq = {
6878c2ecf20Sopenharmony_ci	.read_raw = &tsl2563_read_raw,
6888c2ecf20Sopenharmony_ci	.write_raw = &tsl2563_write_raw,
6898c2ecf20Sopenharmony_ci};
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_cistatic const struct iio_info tsl2563_info = {
6928c2ecf20Sopenharmony_ci	.read_raw = &tsl2563_read_raw,
6938c2ecf20Sopenharmony_ci	.write_raw = &tsl2563_write_raw,
6948c2ecf20Sopenharmony_ci	.read_event_value = &tsl2563_read_thresh,
6958c2ecf20Sopenharmony_ci	.write_event_value = &tsl2563_write_thresh,
6968c2ecf20Sopenharmony_ci	.read_event_config = &tsl2563_read_interrupt_config,
6978c2ecf20Sopenharmony_ci	.write_event_config = &tsl2563_write_interrupt_config,
6988c2ecf20Sopenharmony_ci};
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_cistatic int tsl2563_probe(struct i2c_client *client,
7018c2ecf20Sopenharmony_ci				const struct i2c_device_id *device_id)
7028c2ecf20Sopenharmony_ci{
7038c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev;
7048c2ecf20Sopenharmony_ci	struct tsl2563_chip *chip;
7058c2ecf20Sopenharmony_ci	struct tsl2563_platform_data *pdata = client->dev.platform_data;
7068c2ecf20Sopenharmony_ci	struct device_node *np = client->dev.of_node;
7078c2ecf20Sopenharmony_ci	int err = 0;
7088c2ecf20Sopenharmony_ci	u8 id = 0;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
7118c2ecf20Sopenharmony_ci	if (!indio_dev)
7128c2ecf20Sopenharmony_ci		return -ENOMEM;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	chip = iio_priv(indio_dev);
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, indio_dev);
7178c2ecf20Sopenharmony_ci	chip->client = client;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	err = tsl2563_detect(chip);
7208c2ecf20Sopenharmony_ci	if (err) {
7218c2ecf20Sopenharmony_ci		dev_err(&client->dev, "detect error %d\n", -err);
7228c2ecf20Sopenharmony_ci		return err;
7238c2ecf20Sopenharmony_ci	}
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	err = tsl2563_read_id(chip, &id);
7268c2ecf20Sopenharmony_ci	if (err) {
7278c2ecf20Sopenharmony_ci		dev_err(&client->dev, "read id error %d\n", -err);
7288c2ecf20Sopenharmony_ci		return err;
7298c2ecf20Sopenharmony_ci	}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	mutex_init(&chip->lock);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	/* Default values used until userspace says otherwise */
7348c2ecf20Sopenharmony_ci	chip->low_thres = 0x0;
7358c2ecf20Sopenharmony_ci	chip->high_thres = 0xffff;
7368c2ecf20Sopenharmony_ci	chip->gainlevel = tsl2563_gainlevel_table;
7378c2ecf20Sopenharmony_ci	chip->intr = TSL2563_INT_PERSIST(4);
7388c2ecf20Sopenharmony_ci	chip->calib0 = tsl2563_calib_from_sysfs(CALIB_BASE_SYSFS);
7398c2ecf20Sopenharmony_ci	chip->calib1 = tsl2563_calib_from_sysfs(CALIB_BASE_SYSFS);
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	if (pdata)
7428c2ecf20Sopenharmony_ci		chip->cover_comp_gain = pdata->cover_comp_gain;
7438c2ecf20Sopenharmony_ci	else if (np)
7448c2ecf20Sopenharmony_ci		of_property_read_u32(np, "amstaos,cover-comp-gain",
7458c2ecf20Sopenharmony_ci				     &chip->cover_comp_gain);
7468c2ecf20Sopenharmony_ci	else
7478c2ecf20Sopenharmony_ci		chip->cover_comp_gain = 1;
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	dev_info(&client->dev, "model %d, rev. %d\n", id >> 4, id & 0x0f);
7508c2ecf20Sopenharmony_ci	indio_dev->name = client->name;
7518c2ecf20Sopenharmony_ci	indio_dev->channels = tsl2563_channels;
7528c2ecf20Sopenharmony_ci	indio_dev->num_channels = ARRAY_SIZE(tsl2563_channels);
7538c2ecf20Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	if (client->irq)
7568c2ecf20Sopenharmony_ci		indio_dev->info = &tsl2563_info;
7578c2ecf20Sopenharmony_ci	else
7588c2ecf20Sopenharmony_ci		indio_dev->info = &tsl2563_info_no_irq;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	if (client->irq) {
7618c2ecf20Sopenharmony_ci		err = devm_request_threaded_irq(&client->dev, client->irq,
7628c2ecf20Sopenharmony_ci					   NULL,
7638c2ecf20Sopenharmony_ci					   &tsl2563_event_handler,
7648c2ecf20Sopenharmony_ci					   IRQF_TRIGGER_RISING | IRQF_ONESHOT,
7658c2ecf20Sopenharmony_ci					   "tsl2563_event",
7668c2ecf20Sopenharmony_ci					   indio_dev);
7678c2ecf20Sopenharmony_ci		if (err) {
7688c2ecf20Sopenharmony_ci			dev_err(&client->dev, "irq request error %d\n", -err);
7698c2ecf20Sopenharmony_ci			return err;
7708c2ecf20Sopenharmony_ci		}
7718c2ecf20Sopenharmony_ci	}
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	err = tsl2563_configure(chip);
7748c2ecf20Sopenharmony_ci	if (err) {
7758c2ecf20Sopenharmony_ci		dev_err(&client->dev, "configure error %d\n", -err);
7768c2ecf20Sopenharmony_ci		return err;
7778c2ecf20Sopenharmony_ci	}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&chip->poweroff_work, tsl2563_poweroff_work);
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	/* The interrupt cannot yet be enabled so this is fine without lock */
7828c2ecf20Sopenharmony_ci	schedule_delayed_work(&chip->poweroff_work, 5 * HZ);
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	err = iio_device_register(indio_dev);
7858c2ecf20Sopenharmony_ci	if (err) {
7868c2ecf20Sopenharmony_ci		dev_err(&client->dev, "iio registration error %d\n", -err);
7878c2ecf20Sopenharmony_ci		goto fail;
7888c2ecf20Sopenharmony_ci	}
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	return 0;
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_cifail:
7938c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&chip->poweroff_work);
7948c2ecf20Sopenharmony_ci	return err;
7958c2ecf20Sopenharmony_ci}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_cistatic int tsl2563_remove(struct i2c_client *client)
7988c2ecf20Sopenharmony_ci{
7998c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(client);
8008c2ecf20Sopenharmony_ci	struct tsl2563_chip *chip = iio_priv(indio_dev);
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	iio_device_unregister(indio_dev);
8038c2ecf20Sopenharmony_ci	if (!chip->int_enabled)
8048c2ecf20Sopenharmony_ci		cancel_delayed_work(&chip->poweroff_work);
8058c2ecf20Sopenharmony_ci	/* Ensure that interrupts are disabled - then flush any bottom halves */
8068c2ecf20Sopenharmony_ci	chip->intr &= ~0x30;
8078c2ecf20Sopenharmony_ci	i2c_smbus_write_byte_data(chip->client, TSL2563_CMD | TSL2563_REG_INT,
8088c2ecf20Sopenharmony_ci				  chip->intr);
8098c2ecf20Sopenharmony_ci	flush_scheduled_work();
8108c2ecf20Sopenharmony_ci	tsl2563_set_power(chip, 0);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	return 0;
8138c2ecf20Sopenharmony_ci}
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
8168c2ecf20Sopenharmony_cistatic int tsl2563_suspend(struct device *dev)
8178c2ecf20Sopenharmony_ci{
8188c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
8198c2ecf20Sopenharmony_ci	struct tsl2563_chip *chip = iio_priv(indio_dev);
8208c2ecf20Sopenharmony_ci	int ret;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	mutex_lock(&chip->lock);
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	ret = tsl2563_set_power(chip, 0);
8258c2ecf20Sopenharmony_ci	if (ret)
8268c2ecf20Sopenharmony_ci		goto out;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	chip->suspended = true;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ciout:
8318c2ecf20Sopenharmony_ci	mutex_unlock(&chip->lock);
8328c2ecf20Sopenharmony_ci	return ret;
8338c2ecf20Sopenharmony_ci}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_cistatic int tsl2563_resume(struct device *dev)
8368c2ecf20Sopenharmony_ci{
8378c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
8388c2ecf20Sopenharmony_ci	struct tsl2563_chip *chip = iio_priv(indio_dev);
8398c2ecf20Sopenharmony_ci	int ret;
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	mutex_lock(&chip->lock);
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	ret = tsl2563_set_power(chip, 1);
8448c2ecf20Sopenharmony_ci	if (ret)
8458c2ecf20Sopenharmony_ci		goto out;
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	ret = tsl2563_configure(chip);
8488c2ecf20Sopenharmony_ci	if (ret)
8498c2ecf20Sopenharmony_ci		goto out;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	chip->suspended = false;
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ciout:
8548c2ecf20Sopenharmony_ci	mutex_unlock(&chip->lock);
8558c2ecf20Sopenharmony_ci	return ret;
8568c2ecf20Sopenharmony_ci}
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(tsl2563_pm_ops, tsl2563_suspend, tsl2563_resume);
8598c2ecf20Sopenharmony_ci#define TSL2563_PM_OPS (&tsl2563_pm_ops)
8608c2ecf20Sopenharmony_ci#else
8618c2ecf20Sopenharmony_ci#define TSL2563_PM_OPS NULL
8628c2ecf20Sopenharmony_ci#endif
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_cistatic const struct i2c_device_id tsl2563_id[] = {
8658c2ecf20Sopenharmony_ci	{ "tsl2560", 0 },
8668c2ecf20Sopenharmony_ci	{ "tsl2561", 1 },
8678c2ecf20Sopenharmony_ci	{ "tsl2562", 2 },
8688c2ecf20Sopenharmony_ci	{ "tsl2563", 3 },
8698c2ecf20Sopenharmony_ci	{}
8708c2ecf20Sopenharmony_ci};
8718c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tsl2563_id);
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_cistatic const struct of_device_id tsl2563_of_match[] = {
8748c2ecf20Sopenharmony_ci	{ .compatible = "amstaos,tsl2560" },
8758c2ecf20Sopenharmony_ci	{ .compatible = "amstaos,tsl2561" },
8768c2ecf20Sopenharmony_ci	{ .compatible = "amstaos,tsl2562" },
8778c2ecf20Sopenharmony_ci	{ .compatible = "amstaos,tsl2563" },
8788c2ecf20Sopenharmony_ci	{}
8798c2ecf20Sopenharmony_ci};
8808c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tsl2563_of_match);
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_cistatic struct i2c_driver tsl2563_i2c_driver = {
8838c2ecf20Sopenharmony_ci	.driver = {
8848c2ecf20Sopenharmony_ci		.name	 = "tsl2563",
8858c2ecf20Sopenharmony_ci		.of_match_table = tsl2563_of_match,
8868c2ecf20Sopenharmony_ci		.pm	= TSL2563_PM_OPS,
8878c2ecf20Sopenharmony_ci	},
8888c2ecf20Sopenharmony_ci	.probe		= tsl2563_probe,
8898c2ecf20Sopenharmony_ci	.remove		= tsl2563_remove,
8908c2ecf20Sopenharmony_ci	.id_table	= tsl2563_id,
8918c2ecf20Sopenharmony_ci};
8928c2ecf20Sopenharmony_cimodule_i2c_driver(tsl2563_i2c_driver);
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ciMODULE_AUTHOR("Nokia Corporation");
8958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("tsl2563 light sensor driver");
8968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
897