162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * apds9300.c - IIO driver for Avago APDS9300 ambient light sensor
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2013 Oleksandr Kravchenko <o.v.kravchenko@globallogic.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/slab.h>
1062306a36Sopenharmony_ci#include <linux/pm.h>
1162306a36Sopenharmony_ci#include <linux/i2c.h>
1262306a36Sopenharmony_ci#include <linux/err.h>
1362306a36Sopenharmony_ci#include <linux/mutex.h>
1462306a36Sopenharmony_ci#include <linux/interrupt.h>
1562306a36Sopenharmony_ci#include <linux/iio/iio.h>
1662306a36Sopenharmony_ci#include <linux/iio/sysfs.h>
1762306a36Sopenharmony_ci#include <linux/iio/events.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define APDS9300_DRV_NAME "apds9300"
2062306a36Sopenharmony_ci#define APDS9300_IRQ_NAME "apds9300_event"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/* Command register bits */
2362306a36Sopenharmony_ci#define APDS9300_CMD	BIT(7) /* Select command register. Must write as 1 */
2462306a36Sopenharmony_ci#define APDS9300_WORD	BIT(5) /* I2C write/read: if 1 word, if 0 byte */
2562306a36Sopenharmony_ci#define APDS9300_CLEAR	BIT(6) /* Interrupt clear. Clears pending interrupt */
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/* Register set */
2862306a36Sopenharmony_ci#define APDS9300_CONTROL	0x00 /* Control of basic functions */
2962306a36Sopenharmony_ci#define APDS9300_THRESHLOWLOW	0x02 /* Low byte of low interrupt threshold */
3062306a36Sopenharmony_ci#define APDS9300_THRESHHIGHLOW	0x04 /* Low byte of high interrupt threshold */
3162306a36Sopenharmony_ci#define APDS9300_INTERRUPT	0x06 /* Interrupt control */
3262306a36Sopenharmony_ci#define APDS9300_DATA0LOW	0x0c /* Low byte of ADC channel 0 */
3362306a36Sopenharmony_ci#define APDS9300_DATA1LOW	0x0e /* Low byte of ADC channel 1 */
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/* Power on/off value for APDS9300_CONTROL register */
3662306a36Sopenharmony_ci#define APDS9300_POWER_ON	0x03
3762306a36Sopenharmony_ci#define APDS9300_POWER_OFF	0x00
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/* Interrupts */
4062306a36Sopenharmony_ci#define APDS9300_INTR_ENABLE	0x10
4162306a36Sopenharmony_ci/* Interrupt Persist Function: Any value outside of threshold range */
4262306a36Sopenharmony_ci#define APDS9300_THRESH_INTR	0x01
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define APDS9300_THRESH_MAX	0xffff /* Max threshold value */
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistruct apds9300_data {
4762306a36Sopenharmony_ci	struct i2c_client *client;
4862306a36Sopenharmony_ci	struct mutex mutex;
4962306a36Sopenharmony_ci	int power_state;
5062306a36Sopenharmony_ci	int thresh_low;
5162306a36Sopenharmony_ci	int thresh_hi;
5262306a36Sopenharmony_ci	int intr_en;
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/* Lux calculation */
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/* Calculated values 1000 * (CH1/CH0)^1.4 for CH1/CH0 from 0 to 0.52 */
5862306a36Sopenharmony_cistatic const u16 apds9300_lux_ratio[] = {
5962306a36Sopenharmony_ci	0, 2, 4, 7, 11, 15, 19, 24, 29, 34, 40, 45, 51, 57, 64, 70, 77, 84, 91,
6062306a36Sopenharmony_ci	98, 105, 112, 120, 128, 136, 144, 152, 160, 168, 177, 185, 194, 203,
6162306a36Sopenharmony_ci	212, 221, 230, 239, 249, 258, 268, 277, 287, 297, 307, 317, 327, 337,
6262306a36Sopenharmony_ci	347, 358, 368, 379, 390, 400,
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic unsigned long apds9300_calculate_lux(u16 ch0, u16 ch1)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	unsigned long lux, tmp;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	/* avoid division by zero */
7062306a36Sopenharmony_ci	if (ch0 == 0)
7162306a36Sopenharmony_ci		return 0;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	tmp = DIV_ROUND_UP(ch1 * 100, ch0);
7462306a36Sopenharmony_ci	if (tmp <= 52) {
7562306a36Sopenharmony_ci		lux = 3150 * ch0 - (unsigned long)DIV_ROUND_UP_ULL(ch0
7662306a36Sopenharmony_ci				* apds9300_lux_ratio[tmp] * 5930ull, 1000);
7762306a36Sopenharmony_ci	} else if (tmp <= 65) {
7862306a36Sopenharmony_ci		lux = 2290 * ch0 - 2910 * ch1;
7962306a36Sopenharmony_ci	} else if (tmp <= 80) {
8062306a36Sopenharmony_ci		lux = 1570 * ch0 - 1800 * ch1;
8162306a36Sopenharmony_ci	} else if (tmp <= 130) {
8262306a36Sopenharmony_ci		lux = 338 * ch0 - 260 * ch1;
8362306a36Sopenharmony_ci	} else {
8462306a36Sopenharmony_ci		lux = 0;
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	return lux / 100000;
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic int apds9300_get_adc_val(struct apds9300_data *data, int adc_number)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	int ret;
9362306a36Sopenharmony_ci	u8 flags = APDS9300_CMD | APDS9300_WORD;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (!data->power_state)
9662306a36Sopenharmony_ci		return -EBUSY;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	/* Select ADC0 or ADC1 data register */
9962306a36Sopenharmony_ci	flags |= adc_number ? APDS9300_DATA1LOW : APDS9300_DATA0LOW;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	ret = i2c_smbus_read_word_data(data->client, flags);
10262306a36Sopenharmony_ci	if (ret < 0)
10362306a36Sopenharmony_ci		dev_err(&data->client->dev,
10462306a36Sopenharmony_ci			"failed to read ADC%d value\n", adc_number);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return ret;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic int apds9300_set_thresh_low(struct apds9300_data *data, int value)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	int ret;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (!data->power_state)
11462306a36Sopenharmony_ci		return -EBUSY;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (value > APDS9300_THRESH_MAX)
11762306a36Sopenharmony_ci		return -EINVAL;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	ret = i2c_smbus_write_word_data(data->client, APDS9300_THRESHLOWLOW
12062306a36Sopenharmony_ci			| APDS9300_CMD | APDS9300_WORD, value);
12162306a36Sopenharmony_ci	if (ret) {
12262306a36Sopenharmony_ci		dev_err(&data->client->dev, "failed to set thresh_low\n");
12362306a36Sopenharmony_ci		return ret;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci	data->thresh_low = value;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	return 0;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic int apds9300_set_thresh_hi(struct apds9300_data *data, int value)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	int ret;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	if (!data->power_state)
13562306a36Sopenharmony_ci		return -EBUSY;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (value > APDS9300_THRESH_MAX)
13862306a36Sopenharmony_ci		return -EINVAL;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	ret = i2c_smbus_write_word_data(data->client, APDS9300_THRESHHIGHLOW
14162306a36Sopenharmony_ci			| APDS9300_CMD | APDS9300_WORD, value);
14262306a36Sopenharmony_ci	if (ret) {
14362306a36Sopenharmony_ci		dev_err(&data->client->dev, "failed to set thresh_hi\n");
14462306a36Sopenharmony_ci		return ret;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci	data->thresh_hi = value;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	return 0;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic int apds9300_set_intr_state(struct apds9300_data *data, int state)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	int ret;
15462306a36Sopenharmony_ci	u8 cmd;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (!data->power_state)
15762306a36Sopenharmony_ci		return -EBUSY;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	cmd = state ? APDS9300_INTR_ENABLE | APDS9300_THRESH_INTR : 0x00;
16062306a36Sopenharmony_ci	ret = i2c_smbus_write_byte_data(data->client,
16162306a36Sopenharmony_ci			APDS9300_INTERRUPT | APDS9300_CMD, cmd);
16262306a36Sopenharmony_ci	if (ret) {
16362306a36Sopenharmony_ci		dev_err(&data->client->dev,
16462306a36Sopenharmony_ci			"failed to set interrupt state %d\n", state);
16562306a36Sopenharmony_ci		return ret;
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci	data->intr_en = state;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	return 0;
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic int apds9300_set_power_state(struct apds9300_data *data, int state)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	int ret;
17562306a36Sopenharmony_ci	u8 cmd;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	cmd = state ? APDS9300_POWER_ON : APDS9300_POWER_OFF;
17862306a36Sopenharmony_ci	ret = i2c_smbus_write_byte_data(data->client,
17962306a36Sopenharmony_ci			APDS9300_CONTROL | APDS9300_CMD, cmd);
18062306a36Sopenharmony_ci	if (ret) {
18162306a36Sopenharmony_ci		dev_err(&data->client->dev,
18262306a36Sopenharmony_ci			"failed to set power state %d\n", state);
18362306a36Sopenharmony_ci		return ret;
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci	data->power_state = state;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	return 0;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic void apds9300_clear_intr(struct apds9300_data *data)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	int ret;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	ret = i2c_smbus_write_byte(data->client, APDS9300_CLEAR | APDS9300_CMD);
19562306a36Sopenharmony_ci	if (ret < 0)
19662306a36Sopenharmony_ci		dev_err(&data->client->dev, "failed to clear interrupt\n");
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic int apds9300_chip_init(struct apds9300_data *data)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	int ret;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	/* Need to set power off to ensure that the chip is off */
20462306a36Sopenharmony_ci	ret = apds9300_set_power_state(data, 0);
20562306a36Sopenharmony_ci	if (ret < 0)
20662306a36Sopenharmony_ci		goto err;
20762306a36Sopenharmony_ci	/*
20862306a36Sopenharmony_ci	 * Probe the chip. To do so we try to power up the device and then to
20962306a36Sopenharmony_ci	 * read back the 0x03 code
21062306a36Sopenharmony_ci	 */
21162306a36Sopenharmony_ci	ret = apds9300_set_power_state(data, 1);
21262306a36Sopenharmony_ci	if (ret < 0)
21362306a36Sopenharmony_ci		goto err;
21462306a36Sopenharmony_ci	ret = i2c_smbus_read_byte_data(data->client,
21562306a36Sopenharmony_ci			APDS9300_CONTROL | APDS9300_CMD);
21662306a36Sopenharmony_ci	if (ret != APDS9300_POWER_ON) {
21762306a36Sopenharmony_ci		ret = -ENODEV;
21862306a36Sopenharmony_ci		goto err;
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci	/*
22162306a36Sopenharmony_ci	 * Disable interrupt to ensure thai it is doesn't enable
22262306a36Sopenharmony_ci	 * i.e. after device soft reset
22362306a36Sopenharmony_ci	 */
22462306a36Sopenharmony_ci	ret = apds9300_set_intr_state(data, 0);
22562306a36Sopenharmony_ci	if (ret < 0)
22662306a36Sopenharmony_ci		goto err;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	return 0;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cierr:
23162306a36Sopenharmony_ci	dev_err(&data->client->dev, "failed to init the chip\n");
23262306a36Sopenharmony_ci	return ret;
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic int apds9300_read_raw(struct iio_dev *indio_dev,
23662306a36Sopenharmony_ci		struct iio_chan_spec const *chan, int *val, int *val2,
23762306a36Sopenharmony_ci		long mask)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	int ch0, ch1, ret = -EINVAL;
24062306a36Sopenharmony_ci	struct apds9300_data *data = iio_priv(indio_dev);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	mutex_lock(&data->mutex);
24362306a36Sopenharmony_ci	switch (chan->type) {
24462306a36Sopenharmony_ci	case IIO_LIGHT:
24562306a36Sopenharmony_ci		ch0 = apds9300_get_adc_val(data, 0);
24662306a36Sopenharmony_ci		if (ch0 < 0) {
24762306a36Sopenharmony_ci			ret = ch0;
24862306a36Sopenharmony_ci			break;
24962306a36Sopenharmony_ci		}
25062306a36Sopenharmony_ci		ch1 = apds9300_get_adc_val(data, 1);
25162306a36Sopenharmony_ci		if (ch1 < 0) {
25262306a36Sopenharmony_ci			ret = ch1;
25362306a36Sopenharmony_ci			break;
25462306a36Sopenharmony_ci		}
25562306a36Sopenharmony_ci		*val = apds9300_calculate_lux(ch0, ch1);
25662306a36Sopenharmony_ci		ret = IIO_VAL_INT;
25762306a36Sopenharmony_ci		break;
25862306a36Sopenharmony_ci	case IIO_INTENSITY:
25962306a36Sopenharmony_ci		ret = apds9300_get_adc_val(data, chan->channel);
26062306a36Sopenharmony_ci		if (ret < 0)
26162306a36Sopenharmony_ci			break;
26262306a36Sopenharmony_ci		*val = ret;
26362306a36Sopenharmony_ci		ret = IIO_VAL_INT;
26462306a36Sopenharmony_ci		break;
26562306a36Sopenharmony_ci	default:
26662306a36Sopenharmony_ci		break;
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci	mutex_unlock(&data->mutex);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return ret;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic int apds9300_read_thresh(struct iio_dev *indio_dev,
27462306a36Sopenharmony_ci		const struct iio_chan_spec *chan, enum iio_event_type type,
27562306a36Sopenharmony_ci		enum iio_event_direction dir, enum iio_event_info info,
27662306a36Sopenharmony_ci		int *val, int *val2)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct apds9300_data *data = iio_priv(indio_dev);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	switch (dir) {
28162306a36Sopenharmony_ci	case IIO_EV_DIR_RISING:
28262306a36Sopenharmony_ci		*val = data->thresh_hi;
28362306a36Sopenharmony_ci		break;
28462306a36Sopenharmony_ci	case IIO_EV_DIR_FALLING:
28562306a36Sopenharmony_ci		*val = data->thresh_low;
28662306a36Sopenharmony_ci		break;
28762306a36Sopenharmony_ci	default:
28862306a36Sopenharmony_ci		return -EINVAL;
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	return IIO_VAL_INT;
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic int apds9300_write_thresh(struct iio_dev *indio_dev,
29562306a36Sopenharmony_ci		const struct iio_chan_spec *chan, enum iio_event_type type,
29662306a36Sopenharmony_ci		enum iio_event_direction dir, enum iio_event_info info, int val,
29762306a36Sopenharmony_ci		int val2)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	struct apds9300_data *data = iio_priv(indio_dev);
30062306a36Sopenharmony_ci	int ret;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	mutex_lock(&data->mutex);
30362306a36Sopenharmony_ci	if (dir == IIO_EV_DIR_RISING)
30462306a36Sopenharmony_ci		ret = apds9300_set_thresh_hi(data, val);
30562306a36Sopenharmony_ci	else
30662306a36Sopenharmony_ci		ret = apds9300_set_thresh_low(data, val);
30762306a36Sopenharmony_ci	mutex_unlock(&data->mutex);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	return ret;
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic int apds9300_read_interrupt_config(struct iio_dev *indio_dev,
31362306a36Sopenharmony_ci		const struct iio_chan_spec *chan,
31462306a36Sopenharmony_ci		enum iio_event_type type,
31562306a36Sopenharmony_ci		enum iio_event_direction dir)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	struct apds9300_data *data = iio_priv(indio_dev);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	return data->intr_en;
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic int apds9300_write_interrupt_config(struct iio_dev *indio_dev,
32362306a36Sopenharmony_ci		const struct iio_chan_spec *chan, enum iio_event_type type,
32462306a36Sopenharmony_ci		enum iio_event_direction dir, int state)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	struct apds9300_data *data = iio_priv(indio_dev);
32762306a36Sopenharmony_ci	int ret;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	mutex_lock(&data->mutex);
33062306a36Sopenharmony_ci	ret = apds9300_set_intr_state(data, state);
33162306a36Sopenharmony_ci	mutex_unlock(&data->mutex);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	return ret;
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic const struct iio_info apds9300_info_no_irq = {
33762306a36Sopenharmony_ci	.read_raw	= apds9300_read_raw,
33862306a36Sopenharmony_ci};
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic const struct iio_info apds9300_info = {
34162306a36Sopenharmony_ci	.read_raw		= apds9300_read_raw,
34262306a36Sopenharmony_ci	.read_event_value	= apds9300_read_thresh,
34362306a36Sopenharmony_ci	.write_event_value	= apds9300_write_thresh,
34462306a36Sopenharmony_ci	.read_event_config	= apds9300_read_interrupt_config,
34562306a36Sopenharmony_ci	.write_event_config	= apds9300_write_interrupt_config,
34662306a36Sopenharmony_ci};
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic const struct iio_event_spec apds9300_event_spec[] = {
34962306a36Sopenharmony_ci	{
35062306a36Sopenharmony_ci		.type = IIO_EV_TYPE_THRESH,
35162306a36Sopenharmony_ci		.dir = IIO_EV_DIR_RISING,
35262306a36Sopenharmony_ci		.mask_separate = BIT(IIO_EV_INFO_VALUE) |
35362306a36Sopenharmony_ci			BIT(IIO_EV_INFO_ENABLE),
35462306a36Sopenharmony_ci	}, {
35562306a36Sopenharmony_ci		.type = IIO_EV_TYPE_THRESH,
35662306a36Sopenharmony_ci		.dir = IIO_EV_DIR_FALLING,
35762306a36Sopenharmony_ci		.mask_separate = BIT(IIO_EV_INFO_VALUE) |
35862306a36Sopenharmony_ci			BIT(IIO_EV_INFO_ENABLE),
35962306a36Sopenharmony_ci	},
36062306a36Sopenharmony_ci};
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic const struct iio_chan_spec apds9300_channels[] = {
36362306a36Sopenharmony_ci	{
36462306a36Sopenharmony_ci		.type = IIO_LIGHT,
36562306a36Sopenharmony_ci		.channel = 0,
36662306a36Sopenharmony_ci		.indexed = true,
36762306a36Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
36862306a36Sopenharmony_ci	}, {
36962306a36Sopenharmony_ci		.type = IIO_INTENSITY,
37062306a36Sopenharmony_ci		.channel = 0,
37162306a36Sopenharmony_ci		.channel2 = IIO_MOD_LIGHT_BOTH,
37262306a36Sopenharmony_ci		.indexed = true,
37362306a36Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
37462306a36Sopenharmony_ci		.event_spec = apds9300_event_spec,
37562306a36Sopenharmony_ci		.num_event_specs = ARRAY_SIZE(apds9300_event_spec),
37662306a36Sopenharmony_ci	}, {
37762306a36Sopenharmony_ci		.type = IIO_INTENSITY,
37862306a36Sopenharmony_ci		.channel = 1,
37962306a36Sopenharmony_ci		.channel2 = IIO_MOD_LIGHT_IR,
38062306a36Sopenharmony_ci		.indexed = true,
38162306a36Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
38262306a36Sopenharmony_ci	},
38362306a36Sopenharmony_ci};
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic irqreturn_t apds9300_interrupt_handler(int irq, void *private)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	struct iio_dev *dev_info = private;
38862306a36Sopenharmony_ci	struct apds9300_data *data = iio_priv(dev_info);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	iio_push_event(dev_info,
39162306a36Sopenharmony_ci		       IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0,
39262306a36Sopenharmony_ci					    IIO_EV_TYPE_THRESH,
39362306a36Sopenharmony_ci					    IIO_EV_DIR_EITHER),
39462306a36Sopenharmony_ci		       iio_get_time_ns(dev_info));
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	apds9300_clear_intr(data);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	return IRQ_HANDLED;
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic int apds9300_probe(struct i2c_client *client)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	struct apds9300_data *data;
40462306a36Sopenharmony_ci	struct iio_dev *indio_dev;
40562306a36Sopenharmony_ci	int ret;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
40862306a36Sopenharmony_ci	if (!indio_dev)
40962306a36Sopenharmony_ci		return -ENOMEM;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	data = iio_priv(indio_dev);
41262306a36Sopenharmony_ci	i2c_set_clientdata(client, indio_dev);
41362306a36Sopenharmony_ci	data->client = client;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	ret = apds9300_chip_init(data);
41662306a36Sopenharmony_ci	if (ret < 0)
41762306a36Sopenharmony_ci		goto err;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	mutex_init(&data->mutex);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	indio_dev->channels = apds9300_channels;
42262306a36Sopenharmony_ci	indio_dev->num_channels = ARRAY_SIZE(apds9300_channels);
42362306a36Sopenharmony_ci	indio_dev->name = APDS9300_DRV_NAME;
42462306a36Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	if (client->irq)
42762306a36Sopenharmony_ci		indio_dev->info = &apds9300_info;
42862306a36Sopenharmony_ci	else
42962306a36Sopenharmony_ci		indio_dev->info = &apds9300_info_no_irq;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	if (client->irq) {
43262306a36Sopenharmony_ci		ret = devm_request_threaded_irq(&client->dev, client->irq,
43362306a36Sopenharmony_ci				NULL, apds9300_interrupt_handler,
43462306a36Sopenharmony_ci				IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
43562306a36Sopenharmony_ci				APDS9300_IRQ_NAME, indio_dev);
43662306a36Sopenharmony_ci		if (ret) {
43762306a36Sopenharmony_ci			dev_err(&client->dev, "irq request error %d\n", -ret);
43862306a36Sopenharmony_ci			goto err;
43962306a36Sopenharmony_ci		}
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	ret = iio_device_register(indio_dev);
44362306a36Sopenharmony_ci	if (ret < 0)
44462306a36Sopenharmony_ci		goto err;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	return 0;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cierr:
44962306a36Sopenharmony_ci	/* Ensure that power off in case of error */
45062306a36Sopenharmony_ci	apds9300_set_power_state(data, 0);
45162306a36Sopenharmony_ci	return ret;
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_cistatic void apds9300_remove(struct i2c_client *client)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(client);
45762306a36Sopenharmony_ci	struct apds9300_data *data = iio_priv(indio_dev);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	iio_device_unregister(indio_dev);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	/* Ensure that power off and interrupts are disabled */
46262306a36Sopenharmony_ci	apds9300_set_intr_state(data, 0);
46362306a36Sopenharmony_ci	apds9300_set_power_state(data, 0);
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic int apds9300_suspend(struct device *dev)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
46962306a36Sopenharmony_ci	struct apds9300_data *data = iio_priv(indio_dev);
47062306a36Sopenharmony_ci	int ret;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	mutex_lock(&data->mutex);
47362306a36Sopenharmony_ci	ret = apds9300_set_power_state(data, 0);
47462306a36Sopenharmony_ci	mutex_unlock(&data->mutex);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	return ret;
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_cistatic int apds9300_resume(struct device *dev)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
48262306a36Sopenharmony_ci	struct apds9300_data *data = iio_priv(indio_dev);
48362306a36Sopenharmony_ci	int ret;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	mutex_lock(&data->mutex);
48662306a36Sopenharmony_ci	ret = apds9300_set_power_state(data, 1);
48762306a36Sopenharmony_ci	mutex_unlock(&data->mutex);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	return ret;
49062306a36Sopenharmony_ci}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(apds9300_pm_ops, apds9300_suspend,
49362306a36Sopenharmony_ci				apds9300_resume);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_cistatic const struct i2c_device_id apds9300_id[] = {
49662306a36Sopenharmony_ci	{ APDS9300_DRV_NAME, 0 },
49762306a36Sopenharmony_ci	{ }
49862306a36Sopenharmony_ci};
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, apds9300_id);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic struct i2c_driver apds9300_driver = {
50362306a36Sopenharmony_ci	.driver = {
50462306a36Sopenharmony_ci		.name	= APDS9300_DRV_NAME,
50562306a36Sopenharmony_ci		.pm	= pm_sleep_ptr(&apds9300_pm_ops),
50662306a36Sopenharmony_ci	},
50762306a36Sopenharmony_ci	.probe		= apds9300_probe,
50862306a36Sopenharmony_ci	.remove		= apds9300_remove,
50962306a36Sopenharmony_ci	.id_table	= apds9300_id,
51062306a36Sopenharmony_ci};
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_cimodule_i2c_driver(apds9300_driver);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ciMODULE_AUTHOR("Kravchenko Oleksandr <o.v.kravchenko@globallogic.com>");
51562306a36Sopenharmony_ciMODULE_AUTHOR("GlobalLogic inc.");
51662306a36Sopenharmony_ciMODULE_DESCRIPTION("APDS9300 ambient light photo sensor driver");
51762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
518