162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2013 Samsung Electronics Co., Ltd.
462306a36Sopenharmony_ci * Author: Beomho Seo <beomho.seo@samsung.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/delay.h>
862306a36Sopenharmony_ci#include <linux/err.h>
962306a36Sopenharmony_ci#include <linux/i2c.h>
1062306a36Sopenharmony_ci#include <linux/mutex.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
1462306a36Sopenharmony_ci#include <linux/iio/iio.h>
1562306a36Sopenharmony_ci#include <linux/iio/sysfs.h>
1662306a36Sopenharmony_ci#include <linux/iio/events.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/* Slave address 0x19 for PS of 7 bit addressing protocol for I2C */
1962306a36Sopenharmony_ci#define CM36651_I2C_ADDR_PS		0x19
2062306a36Sopenharmony_ci/* Alert Response Address */
2162306a36Sopenharmony_ci#define CM36651_ARA			0x0C
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* Ambient light sensor */
2462306a36Sopenharmony_ci#define CM36651_CS_CONF1		0x00
2562306a36Sopenharmony_ci#define CM36651_CS_CONF2		0x01
2662306a36Sopenharmony_ci#define CM36651_ALS_WH_M		0x02
2762306a36Sopenharmony_ci#define CM36651_ALS_WH_L		0x03
2862306a36Sopenharmony_ci#define CM36651_ALS_WL_M		0x04
2962306a36Sopenharmony_ci#define CM36651_ALS_WL_L		0x05
3062306a36Sopenharmony_ci#define CM36651_CS_CONF3		0x06
3162306a36Sopenharmony_ci#define CM36651_CS_CONF_REG_NUM		0x02
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* Proximity sensor */
3462306a36Sopenharmony_ci#define CM36651_PS_CONF1		0x00
3562306a36Sopenharmony_ci#define CM36651_PS_THD			0x01
3662306a36Sopenharmony_ci#define CM36651_PS_CANC			0x02
3762306a36Sopenharmony_ci#define CM36651_PS_CONF2		0x03
3862306a36Sopenharmony_ci#define CM36651_PS_REG_NUM		0x04
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/* CS_CONF1 command code */
4162306a36Sopenharmony_ci#define CM36651_ALS_ENABLE		0x00
4262306a36Sopenharmony_ci#define CM36651_ALS_DISABLE		0x01
4362306a36Sopenharmony_ci#define CM36651_ALS_INT_EN		0x02
4462306a36Sopenharmony_ci#define CM36651_ALS_THRES		0x04
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/* CS_CONF2 command code */
4762306a36Sopenharmony_ci#define CM36651_CS_CONF2_DEFAULT_BIT	0x08
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* CS_CONF3 channel integration time */
5062306a36Sopenharmony_ci#define CM36651_CS_IT1			0x00 /* Integration time 80 msec */
5162306a36Sopenharmony_ci#define CM36651_CS_IT2			0x40 /* Integration time 160 msec */
5262306a36Sopenharmony_ci#define CM36651_CS_IT3			0x80 /* Integration time 320 msec */
5362306a36Sopenharmony_ci#define CM36651_CS_IT4			0xC0 /* Integration time 640 msec */
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/* PS_CONF1 command code */
5662306a36Sopenharmony_ci#define CM36651_PS_ENABLE		0x00
5762306a36Sopenharmony_ci#define CM36651_PS_DISABLE		0x01
5862306a36Sopenharmony_ci#define CM36651_PS_INT_EN		0x02
5962306a36Sopenharmony_ci#define CM36651_PS_PERS2		0x04
6062306a36Sopenharmony_ci#define CM36651_PS_PERS3		0x08
6162306a36Sopenharmony_ci#define CM36651_PS_PERS4		0x0C
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/* PS_CONF1 command code: integration time */
6462306a36Sopenharmony_ci#define CM36651_PS_IT1			0x00 /* Integration time 0.32 msec */
6562306a36Sopenharmony_ci#define CM36651_PS_IT2			0x10 /* Integration time 0.42 msec */
6662306a36Sopenharmony_ci#define CM36651_PS_IT3			0x20 /* Integration time 0.52 msec */
6762306a36Sopenharmony_ci#define CM36651_PS_IT4			0x30 /* Integration time 0.64 msec */
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/* PS_CONF1 command code: duty ratio */
7062306a36Sopenharmony_ci#define CM36651_PS_DR1			0x00 /* Duty ratio 1/80 */
7162306a36Sopenharmony_ci#define CM36651_PS_DR2			0x40 /* Duty ratio 1/160 */
7262306a36Sopenharmony_ci#define CM36651_PS_DR3			0x80 /* Duty ratio 1/320 */
7362306a36Sopenharmony_ci#define CM36651_PS_DR4			0xC0 /* Duty ratio 1/640 */
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/* PS_THD command code */
7662306a36Sopenharmony_ci#define CM36651_PS_INITIAL_THD		0x05
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/* PS_CANC command code */
7962306a36Sopenharmony_ci#define CM36651_PS_CANC_DEFAULT		0x00
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci/* PS_CONF2 command code */
8262306a36Sopenharmony_ci#define CM36651_PS_HYS1			0x00
8362306a36Sopenharmony_ci#define CM36651_PS_HYS2			0x01
8462306a36Sopenharmony_ci#define CM36651_PS_SMART_PERS_EN	0x02
8562306a36Sopenharmony_ci#define CM36651_PS_DIR_INT		0x04
8662306a36Sopenharmony_ci#define CM36651_PS_MS			0x10
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci#define CM36651_CS_COLOR_NUM		4
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci#define CM36651_CLOSE_PROXIMITY		0x32
9162306a36Sopenharmony_ci#define CM36651_FAR_PROXIMITY			0x33
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define CM36651_CS_INT_TIME_AVAIL	"0.08 0.16 0.32 0.64"
9462306a36Sopenharmony_ci#define CM36651_PS_INT_TIME_AVAIL	"0.000320 0.000420 0.000520 0.000640"
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cienum cm36651_operation_mode {
9762306a36Sopenharmony_ci	CM36651_LIGHT_EN,
9862306a36Sopenharmony_ci	CM36651_PROXIMITY_EN,
9962306a36Sopenharmony_ci	CM36651_PROXIMITY_EV_EN,
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cienum cm36651_light_channel_idx {
10362306a36Sopenharmony_ci	CM36651_LIGHT_CHANNEL_IDX_RED,
10462306a36Sopenharmony_ci	CM36651_LIGHT_CHANNEL_IDX_GREEN,
10562306a36Sopenharmony_ci	CM36651_LIGHT_CHANNEL_IDX_BLUE,
10662306a36Sopenharmony_ci	CM36651_LIGHT_CHANNEL_IDX_CLEAR,
10762306a36Sopenharmony_ci};
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cienum cm36651_command {
11062306a36Sopenharmony_ci	CM36651_CMD_READ_RAW_LIGHT,
11162306a36Sopenharmony_ci	CM36651_CMD_READ_RAW_PROXIMITY,
11262306a36Sopenharmony_ci	CM36651_CMD_PROX_EV_EN,
11362306a36Sopenharmony_ci	CM36651_CMD_PROX_EV_DIS,
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic const u8 cm36651_cs_reg[CM36651_CS_CONF_REG_NUM] = {
11762306a36Sopenharmony_ci	CM36651_CS_CONF1,
11862306a36Sopenharmony_ci	CM36651_CS_CONF2,
11962306a36Sopenharmony_ci};
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic const u8 cm36651_ps_reg[CM36651_PS_REG_NUM] = {
12262306a36Sopenharmony_ci	CM36651_PS_CONF1,
12362306a36Sopenharmony_ci	CM36651_PS_THD,
12462306a36Sopenharmony_ci	CM36651_PS_CANC,
12562306a36Sopenharmony_ci	CM36651_PS_CONF2,
12662306a36Sopenharmony_ci};
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistruct cm36651_data {
12962306a36Sopenharmony_ci	const struct cm36651_platform_data *pdata;
13062306a36Sopenharmony_ci	struct i2c_client *client;
13162306a36Sopenharmony_ci	struct i2c_client *ps_client;
13262306a36Sopenharmony_ci	struct i2c_client *ara_client;
13362306a36Sopenharmony_ci	struct mutex lock;
13462306a36Sopenharmony_ci	struct regulator *vled_reg;
13562306a36Sopenharmony_ci	unsigned long flags;
13662306a36Sopenharmony_ci	int cs_int_time[CM36651_CS_COLOR_NUM];
13762306a36Sopenharmony_ci	int ps_int_time;
13862306a36Sopenharmony_ci	u8 cs_ctrl_regs[CM36651_CS_CONF_REG_NUM];
13962306a36Sopenharmony_ci	u8 ps_ctrl_regs[CM36651_PS_REG_NUM];
14062306a36Sopenharmony_ci	u16 color[CM36651_CS_COLOR_NUM];
14162306a36Sopenharmony_ci};
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic int cm36651_setup_reg(struct cm36651_data *cm36651)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	struct i2c_client *client = cm36651->client;
14662306a36Sopenharmony_ci	struct i2c_client *ps_client = cm36651->ps_client;
14762306a36Sopenharmony_ci	int i, ret;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/* CS initialization */
15062306a36Sopenharmony_ci	cm36651->cs_ctrl_regs[CM36651_CS_CONF1] = CM36651_ALS_ENABLE |
15162306a36Sopenharmony_ci							     CM36651_ALS_THRES;
15262306a36Sopenharmony_ci	cm36651->cs_ctrl_regs[CM36651_CS_CONF2] = CM36651_CS_CONF2_DEFAULT_BIT;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	for (i = 0; i < CM36651_CS_CONF_REG_NUM; i++) {
15562306a36Sopenharmony_ci		ret = i2c_smbus_write_byte_data(client, cm36651_cs_reg[i],
15662306a36Sopenharmony_ci						     cm36651->cs_ctrl_regs[i]);
15762306a36Sopenharmony_ci		if (ret < 0)
15862306a36Sopenharmony_ci			return ret;
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/* PS initialization */
16262306a36Sopenharmony_ci	cm36651->ps_ctrl_regs[CM36651_PS_CONF1] = CM36651_PS_ENABLE |
16362306a36Sopenharmony_ci								CM36651_PS_IT2;
16462306a36Sopenharmony_ci	cm36651->ps_ctrl_regs[CM36651_PS_THD] = CM36651_PS_INITIAL_THD;
16562306a36Sopenharmony_ci	cm36651->ps_ctrl_regs[CM36651_PS_CANC] = CM36651_PS_CANC_DEFAULT;
16662306a36Sopenharmony_ci	cm36651->ps_ctrl_regs[CM36651_PS_CONF2] = CM36651_PS_HYS2 |
16762306a36Sopenharmony_ci				CM36651_PS_DIR_INT | CM36651_PS_SMART_PERS_EN;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	for (i = 0; i < CM36651_PS_REG_NUM; i++) {
17062306a36Sopenharmony_ci		ret = i2c_smbus_write_byte_data(ps_client, cm36651_ps_reg[i],
17162306a36Sopenharmony_ci						     cm36651->ps_ctrl_regs[i]);
17262306a36Sopenharmony_ci		if (ret < 0)
17362306a36Sopenharmony_ci			return ret;
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	/* Set shutdown mode */
17762306a36Sopenharmony_ci	ret = i2c_smbus_write_byte_data(client, CM36651_CS_CONF1,
17862306a36Sopenharmony_ci							  CM36651_ALS_DISABLE);
17962306a36Sopenharmony_ci	if (ret < 0)
18062306a36Sopenharmony_ci		return ret;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	ret = i2c_smbus_write_byte_data(cm36651->ps_client,
18362306a36Sopenharmony_ci					 CM36651_PS_CONF1, CM36651_PS_DISABLE);
18462306a36Sopenharmony_ci	if (ret < 0)
18562306a36Sopenharmony_ci		return ret;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	return 0;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic int cm36651_read_output(struct cm36651_data *cm36651,
19162306a36Sopenharmony_ci				struct iio_chan_spec const *chan, int *val)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	struct i2c_client *client = cm36651->client;
19462306a36Sopenharmony_ci	int ret = -EINVAL;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	switch (chan->type) {
19762306a36Sopenharmony_ci	case IIO_LIGHT:
19862306a36Sopenharmony_ci		*val = i2c_smbus_read_word_data(client, chan->address);
19962306a36Sopenharmony_ci		if (*val < 0)
20062306a36Sopenharmony_ci			return ret;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci		ret = i2c_smbus_write_byte_data(client, CM36651_CS_CONF1,
20362306a36Sopenharmony_ci							CM36651_ALS_DISABLE);
20462306a36Sopenharmony_ci		if (ret < 0)
20562306a36Sopenharmony_ci			return ret;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci		ret = IIO_VAL_INT;
20862306a36Sopenharmony_ci		break;
20962306a36Sopenharmony_ci	case IIO_PROXIMITY:
21062306a36Sopenharmony_ci		*val = i2c_smbus_read_byte(cm36651->ps_client);
21162306a36Sopenharmony_ci		if (*val < 0)
21262306a36Sopenharmony_ci			return ret;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci		if (!test_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags)) {
21562306a36Sopenharmony_ci			ret = i2c_smbus_write_byte_data(cm36651->ps_client,
21662306a36Sopenharmony_ci					CM36651_PS_CONF1, CM36651_PS_DISABLE);
21762306a36Sopenharmony_ci			if (ret < 0)
21862306a36Sopenharmony_ci				return ret;
21962306a36Sopenharmony_ci		}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci		ret = IIO_VAL_INT;
22262306a36Sopenharmony_ci		break;
22362306a36Sopenharmony_ci	default:
22462306a36Sopenharmony_ci		break;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	return ret;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic irqreturn_t cm36651_irq_handler(int irq, void *data)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct iio_dev *indio_dev = data;
23362306a36Sopenharmony_ci	struct cm36651_data *cm36651 = iio_priv(indio_dev);
23462306a36Sopenharmony_ci	struct i2c_client *client = cm36651->client;
23562306a36Sopenharmony_ci	int ev_dir, ret;
23662306a36Sopenharmony_ci	u64 ev_code;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/*
23962306a36Sopenharmony_ci	 * The PS INT pin is an active low signal that PS INT move logic low
24062306a36Sopenharmony_ci	 * when the object is detect. Once the MCU host received the PS INT
24162306a36Sopenharmony_ci	 * "LOW" signal, the Host needs to read the data at Alert Response
24262306a36Sopenharmony_ci	 * Address(ARA) to clear the PS INT signal. After clearing the PS
24362306a36Sopenharmony_ci	 * INT pin, the PS INT signal toggles from low to high.
24462306a36Sopenharmony_ci	 */
24562306a36Sopenharmony_ci	ret = i2c_smbus_read_byte(cm36651->ara_client);
24662306a36Sopenharmony_ci	if (ret < 0) {
24762306a36Sopenharmony_ci		dev_err(&client->dev,
24862306a36Sopenharmony_ci				"%s: Data read failed: %d\n", __func__, ret);
24962306a36Sopenharmony_ci		return IRQ_HANDLED;
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci	switch (ret) {
25262306a36Sopenharmony_ci	case CM36651_CLOSE_PROXIMITY:
25362306a36Sopenharmony_ci		ev_dir = IIO_EV_DIR_RISING;
25462306a36Sopenharmony_ci		break;
25562306a36Sopenharmony_ci	case CM36651_FAR_PROXIMITY:
25662306a36Sopenharmony_ci		ev_dir = IIO_EV_DIR_FALLING;
25762306a36Sopenharmony_ci		break;
25862306a36Sopenharmony_ci	default:
25962306a36Sopenharmony_ci		dev_err(&client->dev,
26062306a36Sopenharmony_ci			"%s: Data read wrong: %d\n", __func__, ret);
26162306a36Sopenharmony_ci		return IRQ_HANDLED;
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	ev_code = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY,
26562306a36Sopenharmony_ci				CM36651_CMD_READ_RAW_PROXIMITY,
26662306a36Sopenharmony_ci				IIO_EV_TYPE_THRESH, ev_dir);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	iio_push_event(indio_dev, ev_code, iio_get_time_ns(indio_dev));
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return IRQ_HANDLED;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic int cm36651_set_operation_mode(struct cm36651_data *cm36651, int cmd)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct i2c_client *client = cm36651->client;
27662306a36Sopenharmony_ci	struct i2c_client *ps_client = cm36651->ps_client;
27762306a36Sopenharmony_ci	int ret = -EINVAL;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	switch (cmd) {
28062306a36Sopenharmony_ci	case CM36651_CMD_READ_RAW_LIGHT:
28162306a36Sopenharmony_ci		ret = i2c_smbus_write_byte_data(client, CM36651_CS_CONF1,
28262306a36Sopenharmony_ci				cm36651->cs_ctrl_regs[CM36651_CS_CONF1]);
28362306a36Sopenharmony_ci		break;
28462306a36Sopenharmony_ci	case CM36651_CMD_READ_RAW_PROXIMITY:
28562306a36Sopenharmony_ci		if (test_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags))
28662306a36Sopenharmony_ci			return CM36651_PROXIMITY_EV_EN;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci		ret = i2c_smbus_write_byte_data(ps_client, CM36651_PS_CONF1,
28962306a36Sopenharmony_ci				cm36651->ps_ctrl_regs[CM36651_PS_CONF1]);
29062306a36Sopenharmony_ci		break;
29162306a36Sopenharmony_ci	case CM36651_CMD_PROX_EV_EN:
29262306a36Sopenharmony_ci		if (test_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags)) {
29362306a36Sopenharmony_ci			dev_err(&client->dev,
29462306a36Sopenharmony_ci				"Already proximity event enable state\n");
29562306a36Sopenharmony_ci			return ret;
29662306a36Sopenharmony_ci		}
29762306a36Sopenharmony_ci		set_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci		ret = i2c_smbus_write_byte_data(ps_client,
30062306a36Sopenharmony_ci			cm36651_ps_reg[CM36651_PS_CONF1],
30162306a36Sopenharmony_ci			CM36651_PS_INT_EN | CM36651_PS_PERS2 | CM36651_PS_IT2);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci		if (ret < 0) {
30462306a36Sopenharmony_ci			dev_err(&client->dev, "Proximity enable event failed\n");
30562306a36Sopenharmony_ci			return ret;
30662306a36Sopenharmony_ci		}
30762306a36Sopenharmony_ci		break;
30862306a36Sopenharmony_ci	case CM36651_CMD_PROX_EV_DIS:
30962306a36Sopenharmony_ci		if (!test_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags)) {
31062306a36Sopenharmony_ci			dev_err(&client->dev,
31162306a36Sopenharmony_ci				"Already proximity event disable state\n");
31262306a36Sopenharmony_ci			return ret;
31362306a36Sopenharmony_ci		}
31462306a36Sopenharmony_ci		clear_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags);
31562306a36Sopenharmony_ci		ret = i2c_smbus_write_byte_data(ps_client,
31662306a36Sopenharmony_ci					CM36651_PS_CONF1, CM36651_PS_DISABLE);
31762306a36Sopenharmony_ci		break;
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	if (ret < 0)
32162306a36Sopenharmony_ci		dev_err(&client->dev, "Write register failed\n");
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	return ret;
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic int cm36651_read_channel(struct cm36651_data *cm36651,
32762306a36Sopenharmony_ci				struct iio_chan_spec const *chan, int *val)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	struct i2c_client *client = cm36651->client;
33062306a36Sopenharmony_ci	int cmd, ret;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	if (chan->type == IIO_LIGHT)
33362306a36Sopenharmony_ci		cmd = CM36651_CMD_READ_RAW_LIGHT;
33462306a36Sopenharmony_ci	else if (chan->type == IIO_PROXIMITY)
33562306a36Sopenharmony_ci		cmd = CM36651_CMD_READ_RAW_PROXIMITY;
33662306a36Sopenharmony_ci	else
33762306a36Sopenharmony_ci		return -EINVAL;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	ret = cm36651_set_operation_mode(cm36651, cmd);
34062306a36Sopenharmony_ci	if (ret < 0) {
34162306a36Sopenharmony_ci		dev_err(&client->dev, "CM36651 set operation mode failed\n");
34262306a36Sopenharmony_ci		return ret;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci	/* Delay for work after enable operation */
34562306a36Sopenharmony_ci	msleep(50);
34662306a36Sopenharmony_ci	ret = cm36651_read_output(cm36651, chan, val);
34762306a36Sopenharmony_ci	if (ret < 0) {
34862306a36Sopenharmony_ci		dev_err(&client->dev, "CM36651 read output failed\n");
34962306a36Sopenharmony_ci		return ret;
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	return ret;
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic int cm36651_read_int_time(struct cm36651_data *cm36651,
35662306a36Sopenharmony_ci				struct iio_chan_spec const *chan, int *val2)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	switch (chan->type) {
35962306a36Sopenharmony_ci	case IIO_LIGHT:
36062306a36Sopenharmony_ci		if (cm36651->cs_int_time[chan->address] == CM36651_CS_IT1)
36162306a36Sopenharmony_ci			*val2 = 80000;
36262306a36Sopenharmony_ci		else if (cm36651->cs_int_time[chan->address] == CM36651_CS_IT2)
36362306a36Sopenharmony_ci			*val2 = 160000;
36462306a36Sopenharmony_ci		else if (cm36651->cs_int_time[chan->address] == CM36651_CS_IT3)
36562306a36Sopenharmony_ci			*val2 = 320000;
36662306a36Sopenharmony_ci		else if (cm36651->cs_int_time[chan->address] == CM36651_CS_IT4)
36762306a36Sopenharmony_ci			*val2 = 640000;
36862306a36Sopenharmony_ci		else
36962306a36Sopenharmony_ci			return -EINVAL;
37062306a36Sopenharmony_ci		break;
37162306a36Sopenharmony_ci	case IIO_PROXIMITY:
37262306a36Sopenharmony_ci		if (cm36651->ps_int_time == CM36651_PS_IT1)
37362306a36Sopenharmony_ci			*val2 = 320;
37462306a36Sopenharmony_ci		else if (cm36651->ps_int_time == CM36651_PS_IT2)
37562306a36Sopenharmony_ci			*val2 = 420;
37662306a36Sopenharmony_ci		else if (cm36651->ps_int_time == CM36651_PS_IT3)
37762306a36Sopenharmony_ci			*val2 = 520;
37862306a36Sopenharmony_ci		else if (cm36651->ps_int_time == CM36651_PS_IT4)
37962306a36Sopenharmony_ci			*val2 = 640;
38062306a36Sopenharmony_ci		else
38162306a36Sopenharmony_ci			return -EINVAL;
38262306a36Sopenharmony_ci		break;
38362306a36Sopenharmony_ci	default:
38462306a36Sopenharmony_ci		return -EINVAL;
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	return IIO_VAL_INT_PLUS_MICRO;
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_cistatic int cm36651_write_int_time(struct cm36651_data *cm36651,
39162306a36Sopenharmony_ci				struct iio_chan_spec const *chan, int val)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	struct i2c_client *client = cm36651->client;
39462306a36Sopenharmony_ci	struct i2c_client *ps_client = cm36651->ps_client;
39562306a36Sopenharmony_ci	int int_time, ret;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	switch (chan->type) {
39862306a36Sopenharmony_ci	case IIO_LIGHT:
39962306a36Sopenharmony_ci		if (val == 80000)
40062306a36Sopenharmony_ci			int_time = CM36651_CS_IT1;
40162306a36Sopenharmony_ci		else if (val == 160000)
40262306a36Sopenharmony_ci			int_time = CM36651_CS_IT2;
40362306a36Sopenharmony_ci		else if (val == 320000)
40462306a36Sopenharmony_ci			int_time = CM36651_CS_IT3;
40562306a36Sopenharmony_ci		else if (val == 640000)
40662306a36Sopenharmony_ci			int_time = CM36651_CS_IT4;
40762306a36Sopenharmony_ci		else
40862306a36Sopenharmony_ci			return -EINVAL;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci		ret = i2c_smbus_write_byte_data(client, CM36651_CS_CONF3,
41162306a36Sopenharmony_ci					   int_time >> 2 * (chan->address));
41262306a36Sopenharmony_ci		if (ret < 0) {
41362306a36Sopenharmony_ci			dev_err(&client->dev, "CS integration time write failed\n");
41462306a36Sopenharmony_ci			return ret;
41562306a36Sopenharmony_ci		}
41662306a36Sopenharmony_ci		cm36651->cs_int_time[chan->address] = int_time;
41762306a36Sopenharmony_ci		break;
41862306a36Sopenharmony_ci	case IIO_PROXIMITY:
41962306a36Sopenharmony_ci		if (val == 320)
42062306a36Sopenharmony_ci			int_time = CM36651_PS_IT1;
42162306a36Sopenharmony_ci		else if (val == 420)
42262306a36Sopenharmony_ci			int_time = CM36651_PS_IT2;
42362306a36Sopenharmony_ci		else if (val == 520)
42462306a36Sopenharmony_ci			int_time = CM36651_PS_IT3;
42562306a36Sopenharmony_ci		else if (val == 640)
42662306a36Sopenharmony_ci			int_time = CM36651_PS_IT4;
42762306a36Sopenharmony_ci		else
42862306a36Sopenharmony_ci			return -EINVAL;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci		ret = i2c_smbus_write_byte_data(ps_client,
43162306a36Sopenharmony_ci						CM36651_PS_CONF1, int_time);
43262306a36Sopenharmony_ci		if (ret < 0) {
43362306a36Sopenharmony_ci			dev_err(&client->dev, "PS integration time write failed\n");
43462306a36Sopenharmony_ci			return ret;
43562306a36Sopenharmony_ci		}
43662306a36Sopenharmony_ci		cm36651->ps_int_time = int_time;
43762306a36Sopenharmony_ci		break;
43862306a36Sopenharmony_ci	default:
43962306a36Sopenharmony_ci		return -EINVAL;
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	return ret;
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_cistatic int cm36651_read_raw(struct iio_dev *indio_dev,
44662306a36Sopenharmony_ci			    struct iio_chan_spec const *chan,
44762306a36Sopenharmony_ci			    int *val, int *val2, long mask)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct cm36651_data *cm36651 = iio_priv(indio_dev);
45062306a36Sopenharmony_ci	int ret;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	mutex_lock(&cm36651->lock);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	switch (mask) {
45562306a36Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
45662306a36Sopenharmony_ci		ret = cm36651_read_channel(cm36651, chan, val);
45762306a36Sopenharmony_ci		break;
45862306a36Sopenharmony_ci	case IIO_CHAN_INFO_INT_TIME:
45962306a36Sopenharmony_ci		*val = 0;
46062306a36Sopenharmony_ci		ret = cm36651_read_int_time(cm36651, chan, val2);
46162306a36Sopenharmony_ci		break;
46262306a36Sopenharmony_ci	default:
46362306a36Sopenharmony_ci		ret = -EINVAL;
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	mutex_unlock(&cm36651->lock);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	return ret;
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_cistatic int cm36651_write_raw(struct iio_dev *indio_dev,
47262306a36Sopenharmony_ci			     struct iio_chan_spec const *chan,
47362306a36Sopenharmony_ci			     int val, int val2, long mask)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	struct cm36651_data *cm36651 = iio_priv(indio_dev);
47662306a36Sopenharmony_ci	struct i2c_client *client = cm36651->client;
47762306a36Sopenharmony_ci	int ret = -EINVAL;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	if (mask == IIO_CHAN_INFO_INT_TIME) {
48062306a36Sopenharmony_ci		ret = cm36651_write_int_time(cm36651, chan, val2);
48162306a36Sopenharmony_ci		if (ret < 0)
48262306a36Sopenharmony_ci			dev_err(&client->dev, "Integration time write failed\n");
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	return ret;
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_cistatic int cm36651_read_prox_thresh(struct iio_dev *indio_dev,
48962306a36Sopenharmony_ci					const struct iio_chan_spec *chan,
49062306a36Sopenharmony_ci					enum iio_event_type type,
49162306a36Sopenharmony_ci					enum iio_event_direction dir,
49262306a36Sopenharmony_ci					enum iio_event_info info,
49362306a36Sopenharmony_ci					int *val, int *val2)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	struct cm36651_data *cm36651 = iio_priv(indio_dev);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	*val = cm36651->ps_ctrl_regs[CM36651_PS_THD];
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	return 0;
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic int cm36651_write_prox_thresh(struct iio_dev *indio_dev,
50362306a36Sopenharmony_ci					const struct iio_chan_spec *chan,
50462306a36Sopenharmony_ci					enum iio_event_type type,
50562306a36Sopenharmony_ci					enum iio_event_direction dir,
50662306a36Sopenharmony_ci					enum iio_event_info info,
50762306a36Sopenharmony_ci					int val, int val2)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	struct cm36651_data *cm36651 = iio_priv(indio_dev);
51062306a36Sopenharmony_ci	struct i2c_client *client = cm36651->client;
51162306a36Sopenharmony_ci	int ret;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	if (val < 3 || val > 255)
51462306a36Sopenharmony_ci		return -EINVAL;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	cm36651->ps_ctrl_regs[CM36651_PS_THD] = val;
51762306a36Sopenharmony_ci	ret = i2c_smbus_write_byte_data(cm36651->ps_client, CM36651_PS_THD,
51862306a36Sopenharmony_ci					cm36651->ps_ctrl_regs[CM36651_PS_THD]);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	if (ret < 0) {
52162306a36Sopenharmony_ci		dev_err(&client->dev, "PS threshold write failed: %d\n", ret);
52262306a36Sopenharmony_ci		return ret;
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	return 0;
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_cistatic int cm36651_write_prox_event_config(struct iio_dev *indio_dev,
52962306a36Sopenharmony_ci					const struct iio_chan_spec *chan,
53062306a36Sopenharmony_ci					enum iio_event_type type,
53162306a36Sopenharmony_ci					enum iio_event_direction dir,
53262306a36Sopenharmony_ci					int state)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	struct cm36651_data *cm36651 = iio_priv(indio_dev);
53562306a36Sopenharmony_ci	int cmd, ret;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	mutex_lock(&cm36651->lock);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	cmd = state ? CM36651_CMD_PROX_EV_EN : CM36651_CMD_PROX_EV_DIS;
54062306a36Sopenharmony_ci	ret = cm36651_set_operation_mode(cm36651, cmd);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	mutex_unlock(&cm36651->lock);
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	return ret;
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic int cm36651_read_prox_event_config(struct iio_dev *indio_dev,
54862306a36Sopenharmony_ci					const struct iio_chan_spec *chan,
54962306a36Sopenharmony_ci					enum iio_event_type type,
55062306a36Sopenharmony_ci					enum iio_event_direction dir)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	struct cm36651_data *cm36651 = iio_priv(indio_dev);
55362306a36Sopenharmony_ci	int event_en;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	mutex_lock(&cm36651->lock);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	event_en = test_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	mutex_unlock(&cm36651->lock);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	return event_en;
56262306a36Sopenharmony_ci}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci#define CM36651_LIGHT_CHANNEL(_color, _idx) {		\
56562306a36Sopenharmony_ci	.type = IIO_LIGHT,				\
56662306a36Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |	\
56762306a36Sopenharmony_ci			BIT(IIO_CHAN_INFO_INT_TIME),	\
56862306a36Sopenharmony_ci	.address = _idx,				\
56962306a36Sopenharmony_ci	.modified = 1,					\
57062306a36Sopenharmony_ci	.channel2 = IIO_MOD_LIGHT_##_color,		\
57162306a36Sopenharmony_ci}							\
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic const struct iio_event_spec cm36651_event_spec[] = {
57462306a36Sopenharmony_ci	{
57562306a36Sopenharmony_ci		.type = IIO_EV_TYPE_THRESH,
57662306a36Sopenharmony_ci		.dir = IIO_EV_DIR_EITHER,
57762306a36Sopenharmony_ci		.mask_separate = BIT(IIO_EV_INFO_VALUE) |
57862306a36Sopenharmony_ci				BIT(IIO_EV_INFO_ENABLE),
57962306a36Sopenharmony_ci	}
58062306a36Sopenharmony_ci};
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic const struct iio_chan_spec cm36651_channels[] = {
58362306a36Sopenharmony_ci	{
58462306a36Sopenharmony_ci		.type = IIO_PROXIMITY,
58562306a36Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
58662306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_INT_TIME),
58762306a36Sopenharmony_ci		.event_spec = cm36651_event_spec,
58862306a36Sopenharmony_ci		.num_event_specs = ARRAY_SIZE(cm36651_event_spec),
58962306a36Sopenharmony_ci	},
59062306a36Sopenharmony_ci	CM36651_LIGHT_CHANNEL(RED, CM36651_LIGHT_CHANNEL_IDX_RED),
59162306a36Sopenharmony_ci	CM36651_LIGHT_CHANNEL(GREEN, CM36651_LIGHT_CHANNEL_IDX_GREEN),
59262306a36Sopenharmony_ci	CM36651_LIGHT_CHANNEL(BLUE, CM36651_LIGHT_CHANNEL_IDX_BLUE),
59362306a36Sopenharmony_ci	CM36651_LIGHT_CHANNEL(CLEAR, CM36651_LIGHT_CHANNEL_IDX_CLEAR),
59462306a36Sopenharmony_ci};
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cistatic IIO_CONST_ATTR(in_illuminance_integration_time_available,
59762306a36Sopenharmony_ci					CM36651_CS_INT_TIME_AVAIL);
59862306a36Sopenharmony_cistatic IIO_CONST_ATTR(in_proximity_integration_time_available,
59962306a36Sopenharmony_ci					CM36651_PS_INT_TIME_AVAIL);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_cistatic struct attribute *cm36651_attributes[] = {
60262306a36Sopenharmony_ci	&iio_const_attr_in_illuminance_integration_time_available.dev_attr.attr,
60362306a36Sopenharmony_ci	&iio_const_attr_in_proximity_integration_time_available.dev_attr.attr,
60462306a36Sopenharmony_ci	NULL,
60562306a36Sopenharmony_ci};
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic const struct attribute_group cm36651_attribute_group = {
60862306a36Sopenharmony_ci	.attrs = cm36651_attributes
60962306a36Sopenharmony_ci};
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_cistatic const struct iio_info cm36651_info = {
61262306a36Sopenharmony_ci	.read_raw		= &cm36651_read_raw,
61362306a36Sopenharmony_ci	.write_raw		= &cm36651_write_raw,
61462306a36Sopenharmony_ci	.read_event_value	= &cm36651_read_prox_thresh,
61562306a36Sopenharmony_ci	.write_event_value	= &cm36651_write_prox_thresh,
61662306a36Sopenharmony_ci	.read_event_config	= &cm36651_read_prox_event_config,
61762306a36Sopenharmony_ci	.write_event_config	= &cm36651_write_prox_event_config,
61862306a36Sopenharmony_ci	.attrs			= &cm36651_attribute_group,
61962306a36Sopenharmony_ci};
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_cistatic int cm36651_probe(struct i2c_client *client)
62262306a36Sopenharmony_ci{
62362306a36Sopenharmony_ci	const struct i2c_device_id *id = i2c_client_get_device_id(client);
62462306a36Sopenharmony_ci	struct cm36651_data *cm36651;
62562306a36Sopenharmony_ci	struct iio_dev *indio_dev;
62662306a36Sopenharmony_ci	int ret;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*cm36651));
62962306a36Sopenharmony_ci	if (!indio_dev)
63062306a36Sopenharmony_ci		return -ENOMEM;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	cm36651 = iio_priv(indio_dev);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	cm36651->vled_reg = devm_regulator_get(&client->dev, "vled");
63562306a36Sopenharmony_ci	if (IS_ERR(cm36651->vled_reg))
63662306a36Sopenharmony_ci		return dev_err_probe(&client->dev, PTR_ERR(cm36651->vled_reg),
63762306a36Sopenharmony_ci				     "get regulator vled failed\n");
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	ret = regulator_enable(cm36651->vled_reg);
64062306a36Sopenharmony_ci	if (ret) {
64162306a36Sopenharmony_ci		dev_err(&client->dev, "enable regulator vled failed\n");
64262306a36Sopenharmony_ci		return ret;
64362306a36Sopenharmony_ci	}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	i2c_set_clientdata(client, indio_dev);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	cm36651->client = client;
64862306a36Sopenharmony_ci	cm36651->ps_client = i2c_new_dummy_device(client->adapter,
64962306a36Sopenharmony_ci						     CM36651_I2C_ADDR_PS);
65062306a36Sopenharmony_ci	if (IS_ERR(cm36651->ps_client)) {
65162306a36Sopenharmony_ci		dev_err(&client->dev, "%s: new i2c device failed\n", __func__);
65262306a36Sopenharmony_ci		ret = PTR_ERR(cm36651->ps_client);
65362306a36Sopenharmony_ci		goto error_disable_reg;
65462306a36Sopenharmony_ci	}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	cm36651->ara_client = i2c_new_dummy_device(client->adapter, CM36651_ARA);
65762306a36Sopenharmony_ci	if (IS_ERR(cm36651->ara_client)) {
65862306a36Sopenharmony_ci		dev_err(&client->dev, "%s: new i2c device failed\n", __func__);
65962306a36Sopenharmony_ci		ret = PTR_ERR(cm36651->ara_client);
66062306a36Sopenharmony_ci		goto error_i2c_unregister_ps;
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	mutex_init(&cm36651->lock);
66462306a36Sopenharmony_ci	indio_dev->channels = cm36651_channels;
66562306a36Sopenharmony_ci	indio_dev->num_channels = ARRAY_SIZE(cm36651_channels);
66662306a36Sopenharmony_ci	indio_dev->info = &cm36651_info;
66762306a36Sopenharmony_ci	indio_dev->name = id->name;
66862306a36Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	ret = cm36651_setup_reg(cm36651);
67162306a36Sopenharmony_ci	if (ret) {
67262306a36Sopenharmony_ci		dev_err(&client->dev, "%s: register setup failed\n", __func__);
67362306a36Sopenharmony_ci		goto error_i2c_unregister_ara;
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	ret = request_threaded_irq(client->irq, NULL, cm36651_irq_handler,
67762306a36Sopenharmony_ci					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
67862306a36Sopenharmony_ci							"cm36651", indio_dev);
67962306a36Sopenharmony_ci	if (ret) {
68062306a36Sopenharmony_ci		dev_err(&client->dev, "%s: request irq failed\n", __func__);
68162306a36Sopenharmony_ci		goto error_i2c_unregister_ara;
68262306a36Sopenharmony_ci	}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	ret = iio_device_register(indio_dev);
68562306a36Sopenharmony_ci	if (ret) {
68662306a36Sopenharmony_ci		dev_err(&client->dev, "%s: regist device failed\n", __func__);
68762306a36Sopenharmony_ci		goto error_free_irq;
68862306a36Sopenharmony_ci	}
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	return 0;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_cierror_free_irq:
69362306a36Sopenharmony_ci	free_irq(client->irq, indio_dev);
69462306a36Sopenharmony_cierror_i2c_unregister_ara:
69562306a36Sopenharmony_ci	i2c_unregister_device(cm36651->ara_client);
69662306a36Sopenharmony_cierror_i2c_unregister_ps:
69762306a36Sopenharmony_ci	i2c_unregister_device(cm36651->ps_client);
69862306a36Sopenharmony_cierror_disable_reg:
69962306a36Sopenharmony_ci	regulator_disable(cm36651->vled_reg);
70062306a36Sopenharmony_ci	return ret;
70162306a36Sopenharmony_ci}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_cistatic void cm36651_remove(struct i2c_client *client)
70462306a36Sopenharmony_ci{
70562306a36Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(client);
70662306a36Sopenharmony_ci	struct cm36651_data *cm36651 = iio_priv(indio_dev);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	iio_device_unregister(indio_dev);
70962306a36Sopenharmony_ci	regulator_disable(cm36651->vled_reg);
71062306a36Sopenharmony_ci	free_irq(client->irq, indio_dev);
71162306a36Sopenharmony_ci	i2c_unregister_device(cm36651->ps_client);
71262306a36Sopenharmony_ci	i2c_unregister_device(cm36651->ara_client);
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_cistatic const struct i2c_device_id cm36651_id[] = {
71662306a36Sopenharmony_ci	{ "cm36651", 0 },
71762306a36Sopenharmony_ci	{ }
71862306a36Sopenharmony_ci};
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, cm36651_id);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_cistatic const struct of_device_id cm36651_of_match[] = {
72362306a36Sopenharmony_ci	{ .compatible = "capella,cm36651" },
72462306a36Sopenharmony_ci	{ }
72562306a36Sopenharmony_ci};
72662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, cm36651_of_match);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_cistatic struct i2c_driver cm36651_driver = {
72962306a36Sopenharmony_ci	.driver = {
73062306a36Sopenharmony_ci		.name	= "cm36651",
73162306a36Sopenharmony_ci		.of_match_table = cm36651_of_match,
73262306a36Sopenharmony_ci	},
73362306a36Sopenharmony_ci	.probe		= cm36651_probe,
73462306a36Sopenharmony_ci	.remove		= cm36651_remove,
73562306a36Sopenharmony_ci	.id_table	= cm36651_id,
73662306a36Sopenharmony_ci};
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_cimodule_i2c_driver(cm36651_driver);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ciMODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>");
74162306a36Sopenharmony_ciMODULE_DESCRIPTION("CM36651 proximity/ambient light sensor driver");
74262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
743