162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * A iio driver for the light sensor ISL 29018/29023/29035.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * IIO driver for monitoring ambient light intensity in luxi, proximity
662306a36Sopenharmony_ci * sensing and infrared sensing.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Copyright (c) 2010, NVIDIA Corporation.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/i2c.h>
1362306a36Sopenharmony_ci#include <linux/err.h>
1462306a36Sopenharmony_ci#include <linux/mutex.h>
1562306a36Sopenharmony_ci#include <linux/delay.h>
1662306a36Sopenharmony_ci#include <linux/regmap.h>
1762306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci#include <linux/iio/iio.h>
2062306a36Sopenharmony_ci#include <linux/iio/sysfs.h>
2162306a36Sopenharmony_ci#include <linux/acpi.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define ISL29018_CONV_TIME_MS		100
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define ISL29018_REG_ADD_COMMAND1	0x00
2662306a36Sopenharmony_ci#define ISL29018_CMD1_OPMODE_SHIFT	5
2762306a36Sopenharmony_ci#define ISL29018_CMD1_OPMODE_MASK	(7 << ISL29018_CMD1_OPMODE_SHIFT)
2862306a36Sopenharmony_ci#define ISL29018_CMD1_OPMODE_POWER_DOWN	0
2962306a36Sopenharmony_ci#define ISL29018_CMD1_OPMODE_ALS_ONCE	1
3062306a36Sopenharmony_ci#define ISL29018_CMD1_OPMODE_IR_ONCE	2
3162306a36Sopenharmony_ci#define ISL29018_CMD1_OPMODE_PROX_ONCE	3
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define ISL29018_REG_ADD_COMMAND2	0x01
3462306a36Sopenharmony_ci#define ISL29018_CMD2_RESOLUTION_SHIFT	2
3562306a36Sopenharmony_ci#define ISL29018_CMD2_RESOLUTION_MASK	(0x3 << ISL29018_CMD2_RESOLUTION_SHIFT)
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define ISL29018_CMD2_RANGE_SHIFT	0
3862306a36Sopenharmony_ci#define ISL29018_CMD2_RANGE_MASK	(0x3 << ISL29018_CMD2_RANGE_SHIFT)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define ISL29018_CMD2_SCHEME_SHIFT	7
4162306a36Sopenharmony_ci#define ISL29018_CMD2_SCHEME_MASK	(0x1 << ISL29018_CMD2_SCHEME_SHIFT)
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define ISL29018_REG_ADD_DATA_LSB	0x02
4462306a36Sopenharmony_ci#define ISL29018_REG_ADD_DATA_MSB	0x03
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define ISL29018_REG_TEST		0x08
4762306a36Sopenharmony_ci#define ISL29018_TEST_SHIFT		0
4862306a36Sopenharmony_ci#define ISL29018_TEST_MASK		(0xFF << ISL29018_TEST_SHIFT)
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#define ISL29035_REG_DEVICE_ID		0x0F
5162306a36Sopenharmony_ci#define ISL29035_DEVICE_ID_SHIFT	0x03
5262306a36Sopenharmony_ci#define ISL29035_DEVICE_ID_MASK		(0x7 << ISL29035_DEVICE_ID_SHIFT)
5362306a36Sopenharmony_ci#define ISL29035_DEVICE_ID		0x5
5462306a36Sopenharmony_ci#define ISL29035_BOUT_SHIFT		0x07
5562306a36Sopenharmony_ci#define ISL29035_BOUT_MASK		(0x01 << ISL29035_BOUT_SHIFT)
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cienum isl29018_int_time {
5862306a36Sopenharmony_ci	ISL29018_INT_TIME_16,
5962306a36Sopenharmony_ci	ISL29018_INT_TIME_12,
6062306a36Sopenharmony_ci	ISL29018_INT_TIME_8,
6162306a36Sopenharmony_ci	ISL29018_INT_TIME_4,
6262306a36Sopenharmony_ci};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic const unsigned int isl29018_int_utimes[3][4] = {
6562306a36Sopenharmony_ci	{90000, 5630, 351, 21},
6662306a36Sopenharmony_ci	{90000, 5600, 352, 22},
6762306a36Sopenharmony_ci	{105000, 6500, 410, 25},
6862306a36Sopenharmony_ci};
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic const struct isl29018_scale {
7162306a36Sopenharmony_ci	unsigned int scale;
7262306a36Sopenharmony_ci	unsigned int uscale;
7362306a36Sopenharmony_ci} isl29018_scales[4][4] = {
7462306a36Sopenharmony_ci	{ {0, 15258}, {0, 61035}, {0, 244140}, {0, 976562} },
7562306a36Sopenharmony_ci	{ {0, 244140}, {0, 976562}, {3, 906250}, {15, 625000} },
7662306a36Sopenharmony_ci	{ {3, 906250}, {15, 625000}, {62, 500000}, {250, 0} },
7762306a36Sopenharmony_ci	{ {62, 500000}, {250, 0}, {1000, 0}, {4000, 0} }
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistruct isl29018_chip {
8162306a36Sopenharmony_ci	struct regmap		*regmap;
8262306a36Sopenharmony_ci	struct mutex		lock;
8362306a36Sopenharmony_ci	int			type;
8462306a36Sopenharmony_ci	unsigned int		calibscale;
8562306a36Sopenharmony_ci	unsigned int		ucalibscale;
8662306a36Sopenharmony_ci	unsigned int		int_time;
8762306a36Sopenharmony_ci	struct isl29018_scale	scale;
8862306a36Sopenharmony_ci	int			prox_scheme;
8962306a36Sopenharmony_ci	bool			suspended;
9062306a36Sopenharmony_ci	struct regulator	*vcc_reg;
9162306a36Sopenharmony_ci};
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic int isl29018_set_integration_time(struct isl29018_chip *chip,
9462306a36Sopenharmony_ci					 unsigned int utime)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	unsigned int i;
9762306a36Sopenharmony_ci	int ret;
9862306a36Sopenharmony_ci	unsigned int int_time, new_int_time;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(isl29018_int_utimes[chip->type]); ++i) {
10162306a36Sopenharmony_ci		if (utime == isl29018_int_utimes[chip->type][i]) {
10262306a36Sopenharmony_ci			new_int_time = i;
10362306a36Sopenharmony_ci			break;
10462306a36Sopenharmony_ci		}
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	if (i >= ARRAY_SIZE(isl29018_int_utimes[chip->type]))
10862306a36Sopenharmony_ci		return -EINVAL;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMAND2,
11162306a36Sopenharmony_ci				 ISL29018_CMD2_RESOLUTION_MASK,
11262306a36Sopenharmony_ci				 i << ISL29018_CMD2_RESOLUTION_SHIFT);
11362306a36Sopenharmony_ci	if (ret < 0)
11462306a36Sopenharmony_ci		return ret;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	/* Keep the same range when integration time changes */
11762306a36Sopenharmony_ci	int_time = chip->int_time;
11862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(isl29018_scales[int_time]); ++i) {
11962306a36Sopenharmony_ci		if (chip->scale.scale == isl29018_scales[int_time][i].scale &&
12062306a36Sopenharmony_ci		    chip->scale.uscale == isl29018_scales[int_time][i].uscale) {
12162306a36Sopenharmony_ci			chip->scale = isl29018_scales[new_int_time][i];
12262306a36Sopenharmony_ci			break;
12362306a36Sopenharmony_ci		}
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci	chip->int_time = new_int_time;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	return 0;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic int isl29018_set_scale(struct isl29018_chip *chip, int scale, int uscale)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	unsigned int i;
13362306a36Sopenharmony_ci	int ret;
13462306a36Sopenharmony_ci	struct isl29018_scale new_scale;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(isl29018_scales[chip->int_time]); ++i) {
13762306a36Sopenharmony_ci		if (scale == isl29018_scales[chip->int_time][i].scale &&
13862306a36Sopenharmony_ci		    uscale == isl29018_scales[chip->int_time][i].uscale) {
13962306a36Sopenharmony_ci			new_scale = isl29018_scales[chip->int_time][i];
14062306a36Sopenharmony_ci			break;
14162306a36Sopenharmony_ci		}
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	if (i >= ARRAY_SIZE(isl29018_scales[chip->int_time]))
14562306a36Sopenharmony_ci		return -EINVAL;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMAND2,
14862306a36Sopenharmony_ci				 ISL29018_CMD2_RANGE_MASK,
14962306a36Sopenharmony_ci				 i << ISL29018_CMD2_RANGE_SHIFT);
15062306a36Sopenharmony_ci	if (ret < 0)
15162306a36Sopenharmony_ci		return ret;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	chip->scale = new_scale;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	return 0;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic int isl29018_read_sensor_input(struct isl29018_chip *chip, int mode)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	int status;
16162306a36Sopenharmony_ci	unsigned int lsb;
16262306a36Sopenharmony_ci	unsigned int msb;
16362306a36Sopenharmony_ci	struct device *dev = regmap_get_device(chip->regmap);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	/* Set mode */
16662306a36Sopenharmony_ci	status = regmap_write(chip->regmap, ISL29018_REG_ADD_COMMAND1,
16762306a36Sopenharmony_ci			      mode << ISL29018_CMD1_OPMODE_SHIFT);
16862306a36Sopenharmony_ci	if (status) {
16962306a36Sopenharmony_ci		dev_err(dev,
17062306a36Sopenharmony_ci			"Error in setting operating mode err %d\n", status);
17162306a36Sopenharmony_ci		return status;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci	msleep(ISL29018_CONV_TIME_MS);
17462306a36Sopenharmony_ci	status = regmap_read(chip->regmap, ISL29018_REG_ADD_DATA_LSB, &lsb);
17562306a36Sopenharmony_ci	if (status < 0) {
17662306a36Sopenharmony_ci		dev_err(dev,
17762306a36Sopenharmony_ci			"Error in reading LSB DATA with err %d\n", status);
17862306a36Sopenharmony_ci		return status;
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	status = regmap_read(chip->regmap, ISL29018_REG_ADD_DATA_MSB, &msb);
18262306a36Sopenharmony_ci	if (status < 0) {
18362306a36Sopenharmony_ci		dev_err(dev,
18462306a36Sopenharmony_ci			"Error in reading MSB DATA with error %d\n", status);
18562306a36Sopenharmony_ci		return status;
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci	dev_vdbg(dev, "MSB 0x%x and LSB 0x%x\n", msb, lsb);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	return (msb << 8) | lsb;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic int isl29018_read_lux(struct isl29018_chip *chip, int *lux)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	int lux_data;
19562306a36Sopenharmony_ci	unsigned int data_x_range;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	lux_data = isl29018_read_sensor_input(chip,
19862306a36Sopenharmony_ci					      ISL29018_CMD1_OPMODE_ALS_ONCE);
19962306a36Sopenharmony_ci	if (lux_data < 0)
20062306a36Sopenharmony_ci		return lux_data;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	data_x_range = lux_data * chip->scale.scale +
20362306a36Sopenharmony_ci		       lux_data * chip->scale.uscale / 1000000;
20462306a36Sopenharmony_ci	*lux = data_x_range * chip->calibscale +
20562306a36Sopenharmony_ci	       data_x_range * chip->ucalibscale / 1000000;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	return 0;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic int isl29018_read_ir(struct isl29018_chip *chip, int *ir)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	int ir_data;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	ir_data = isl29018_read_sensor_input(chip,
21562306a36Sopenharmony_ci					     ISL29018_CMD1_OPMODE_IR_ONCE);
21662306a36Sopenharmony_ci	if (ir_data < 0)
21762306a36Sopenharmony_ci		return ir_data;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	*ir = ir_data;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	return 0;
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic int isl29018_read_proximity_ir(struct isl29018_chip *chip, int scheme,
22562306a36Sopenharmony_ci				      int *near_ir)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	int status;
22862306a36Sopenharmony_ci	int prox_data = -1;
22962306a36Sopenharmony_ci	int ir_data = -1;
23062306a36Sopenharmony_ci	struct device *dev = regmap_get_device(chip->regmap);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	/* Do proximity sensing with required scheme */
23362306a36Sopenharmony_ci	status = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMAND2,
23462306a36Sopenharmony_ci				    ISL29018_CMD2_SCHEME_MASK,
23562306a36Sopenharmony_ci				    scheme << ISL29018_CMD2_SCHEME_SHIFT);
23662306a36Sopenharmony_ci	if (status) {
23762306a36Sopenharmony_ci		dev_err(dev, "Error in setting operating mode\n");
23862306a36Sopenharmony_ci		return status;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	prox_data = isl29018_read_sensor_input(chip,
24262306a36Sopenharmony_ci					       ISL29018_CMD1_OPMODE_PROX_ONCE);
24362306a36Sopenharmony_ci	if (prox_data < 0)
24462306a36Sopenharmony_ci		return prox_data;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (scheme == 1) {
24762306a36Sopenharmony_ci		*near_ir = prox_data;
24862306a36Sopenharmony_ci		return 0;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	ir_data = isl29018_read_sensor_input(chip,
25262306a36Sopenharmony_ci					     ISL29018_CMD1_OPMODE_IR_ONCE);
25362306a36Sopenharmony_ci	if (ir_data < 0)
25462306a36Sopenharmony_ci		return ir_data;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	if (prox_data >= ir_data)
25762306a36Sopenharmony_ci		*near_ir = prox_data - ir_data;
25862306a36Sopenharmony_ci	else
25962306a36Sopenharmony_ci		*near_ir = 0;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	return 0;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic ssize_t in_illuminance_scale_available_show
26562306a36Sopenharmony_ci			(struct device *dev, struct device_attribute *attr,
26662306a36Sopenharmony_ci			 char *buf)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
26962306a36Sopenharmony_ci	struct isl29018_chip *chip = iio_priv(indio_dev);
27062306a36Sopenharmony_ci	unsigned int i;
27162306a36Sopenharmony_ci	int len = 0;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	mutex_lock(&chip->lock);
27462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(isl29018_scales[chip->int_time]); ++i)
27562306a36Sopenharmony_ci		len += sprintf(buf + len, "%d.%06d ",
27662306a36Sopenharmony_ci			       isl29018_scales[chip->int_time][i].scale,
27762306a36Sopenharmony_ci			       isl29018_scales[chip->int_time][i].uscale);
27862306a36Sopenharmony_ci	mutex_unlock(&chip->lock);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	buf[len - 1] = '\n';
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	return len;
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic ssize_t in_illuminance_integration_time_available_show
28662306a36Sopenharmony_ci			(struct device *dev, struct device_attribute *attr,
28762306a36Sopenharmony_ci			 char *buf)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
29062306a36Sopenharmony_ci	struct isl29018_chip *chip = iio_priv(indio_dev);
29162306a36Sopenharmony_ci	unsigned int i;
29262306a36Sopenharmony_ci	int len = 0;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(isl29018_int_utimes[chip->type]); ++i)
29562306a36Sopenharmony_ci		len += sprintf(buf + len, "0.%06d ",
29662306a36Sopenharmony_ci			       isl29018_int_utimes[chip->type][i]);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	buf[len - 1] = '\n';
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	return len;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci/*
30462306a36Sopenharmony_ci * From ISL29018 Data Sheet (FN6619.4, Oct 8, 2012) regarding the
30562306a36Sopenharmony_ci * infrared suppression:
30662306a36Sopenharmony_ci *
30762306a36Sopenharmony_ci *   Proximity Sensing Scheme: Bit 7. This bit programs the function
30862306a36Sopenharmony_ci * of the proximity detection. Logic 0 of this bit, Scheme 0, makes
30962306a36Sopenharmony_ci * full n (4, 8, 12, 16) bits (unsigned) proximity detection. The range
31062306a36Sopenharmony_ci * of Scheme 0 proximity count is from 0 to 2^n. Logic 1 of this bit,
31162306a36Sopenharmony_ci * Scheme 1, makes n-1 (3, 7, 11, 15) bits (2's complementary)
31262306a36Sopenharmony_ci * proximity_less_ambient detection. The range of Scheme 1
31362306a36Sopenharmony_ci * proximity count is from -2^(n-1) to 2^(n-1) . The sign bit is extended
31462306a36Sopenharmony_ci * for resolutions less than 16. While Scheme 0 has wider dynamic
31562306a36Sopenharmony_ci * range, Scheme 1 proximity detection is less affected by the
31662306a36Sopenharmony_ci * ambient IR noise variation.
31762306a36Sopenharmony_ci *
31862306a36Sopenharmony_ci * 0 Sensing IR from LED and ambient
31962306a36Sopenharmony_ci * 1 Sensing IR from LED with ambient IR rejection
32062306a36Sopenharmony_ci */
32162306a36Sopenharmony_cistatic ssize_t proximity_on_chip_ambient_infrared_suppression_show
32262306a36Sopenharmony_ci			(struct device *dev, struct device_attribute *attr,
32362306a36Sopenharmony_ci			 char *buf)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
32662306a36Sopenharmony_ci	struct isl29018_chip *chip = iio_priv(indio_dev);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	/*
32962306a36Sopenharmony_ci	 * Return the "proximity scheme" i.e. if the chip does on chip
33062306a36Sopenharmony_ci	 * infrared suppression (1 means perform on chip suppression)
33162306a36Sopenharmony_ci	 */
33262306a36Sopenharmony_ci	return sprintf(buf, "%d\n", chip->prox_scheme);
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic ssize_t proximity_on_chip_ambient_infrared_suppression_store
33662306a36Sopenharmony_ci			(struct device *dev, struct device_attribute *attr,
33762306a36Sopenharmony_ci			 const char *buf, size_t count)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
34062306a36Sopenharmony_ci	struct isl29018_chip *chip = iio_priv(indio_dev);
34162306a36Sopenharmony_ci	int val;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if (kstrtoint(buf, 10, &val))
34462306a36Sopenharmony_ci		return -EINVAL;
34562306a36Sopenharmony_ci	if (!(val == 0 || val == 1))
34662306a36Sopenharmony_ci		return -EINVAL;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	/*
34962306a36Sopenharmony_ci	 * Get the "proximity scheme" i.e. if the chip does on chip
35062306a36Sopenharmony_ci	 * infrared suppression (1 means perform on chip suppression)
35162306a36Sopenharmony_ci	 */
35262306a36Sopenharmony_ci	mutex_lock(&chip->lock);
35362306a36Sopenharmony_ci	chip->prox_scheme = val;
35462306a36Sopenharmony_ci	mutex_unlock(&chip->lock);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	return count;
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic int isl29018_write_raw(struct iio_dev *indio_dev,
36062306a36Sopenharmony_ci			      struct iio_chan_spec const *chan,
36162306a36Sopenharmony_ci			      int val,
36262306a36Sopenharmony_ci			      int val2,
36362306a36Sopenharmony_ci			      long mask)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	struct isl29018_chip *chip = iio_priv(indio_dev);
36662306a36Sopenharmony_ci	int ret = -EINVAL;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	mutex_lock(&chip->lock);
36962306a36Sopenharmony_ci	if (chip->suspended) {
37062306a36Sopenharmony_ci		ret = -EBUSY;
37162306a36Sopenharmony_ci		goto write_done;
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci	switch (mask) {
37462306a36Sopenharmony_ci	case IIO_CHAN_INFO_CALIBSCALE:
37562306a36Sopenharmony_ci		if (chan->type == IIO_LIGHT) {
37662306a36Sopenharmony_ci			chip->calibscale = val;
37762306a36Sopenharmony_ci			chip->ucalibscale = val2;
37862306a36Sopenharmony_ci			ret = 0;
37962306a36Sopenharmony_ci		}
38062306a36Sopenharmony_ci		break;
38162306a36Sopenharmony_ci	case IIO_CHAN_INFO_INT_TIME:
38262306a36Sopenharmony_ci		if (chan->type == IIO_LIGHT && !val)
38362306a36Sopenharmony_ci			ret = isl29018_set_integration_time(chip, val2);
38462306a36Sopenharmony_ci		break;
38562306a36Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
38662306a36Sopenharmony_ci		if (chan->type == IIO_LIGHT)
38762306a36Sopenharmony_ci			ret = isl29018_set_scale(chip, val, val2);
38862306a36Sopenharmony_ci		break;
38962306a36Sopenharmony_ci	default:
39062306a36Sopenharmony_ci		break;
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ciwrite_done:
39462306a36Sopenharmony_ci	mutex_unlock(&chip->lock);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	return ret;
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic int isl29018_read_raw(struct iio_dev *indio_dev,
40062306a36Sopenharmony_ci			     struct iio_chan_spec const *chan,
40162306a36Sopenharmony_ci			     int *val,
40262306a36Sopenharmony_ci			     int *val2,
40362306a36Sopenharmony_ci			     long mask)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	int ret = -EINVAL;
40662306a36Sopenharmony_ci	struct isl29018_chip *chip = iio_priv(indio_dev);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	mutex_lock(&chip->lock);
40962306a36Sopenharmony_ci	if (chip->suspended) {
41062306a36Sopenharmony_ci		ret = -EBUSY;
41162306a36Sopenharmony_ci		goto read_done;
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci	switch (mask) {
41462306a36Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
41562306a36Sopenharmony_ci	case IIO_CHAN_INFO_PROCESSED:
41662306a36Sopenharmony_ci		switch (chan->type) {
41762306a36Sopenharmony_ci		case IIO_LIGHT:
41862306a36Sopenharmony_ci			ret = isl29018_read_lux(chip, val);
41962306a36Sopenharmony_ci			break;
42062306a36Sopenharmony_ci		case IIO_INTENSITY:
42162306a36Sopenharmony_ci			ret = isl29018_read_ir(chip, val);
42262306a36Sopenharmony_ci			break;
42362306a36Sopenharmony_ci		case IIO_PROXIMITY:
42462306a36Sopenharmony_ci			ret = isl29018_read_proximity_ir(chip,
42562306a36Sopenharmony_ci							 chip->prox_scheme,
42662306a36Sopenharmony_ci							 val);
42762306a36Sopenharmony_ci			break;
42862306a36Sopenharmony_ci		default:
42962306a36Sopenharmony_ci			break;
43062306a36Sopenharmony_ci		}
43162306a36Sopenharmony_ci		if (!ret)
43262306a36Sopenharmony_ci			ret = IIO_VAL_INT;
43362306a36Sopenharmony_ci		break;
43462306a36Sopenharmony_ci	case IIO_CHAN_INFO_INT_TIME:
43562306a36Sopenharmony_ci		if (chan->type == IIO_LIGHT) {
43662306a36Sopenharmony_ci			*val = 0;
43762306a36Sopenharmony_ci			*val2 = isl29018_int_utimes[chip->type][chip->int_time];
43862306a36Sopenharmony_ci			ret = IIO_VAL_INT_PLUS_MICRO;
43962306a36Sopenharmony_ci		}
44062306a36Sopenharmony_ci		break;
44162306a36Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
44262306a36Sopenharmony_ci		if (chan->type == IIO_LIGHT) {
44362306a36Sopenharmony_ci			*val = chip->scale.scale;
44462306a36Sopenharmony_ci			*val2 = chip->scale.uscale;
44562306a36Sopenharmony_ci			ret = IIO_VAL_INT_PLUS_MICRO;
44662306a36Sopenharmony_ci		}
44762306a36Sopenharmony_ci		break;
44862306a36Sopenharmony_ci	case IIO_CHAN_INFO_CALIBSCALE:
44962306a36Sopenharmony_ci		if (chan->type == IIO_LIGHT) {
45062306a36Sopenharmony_ci			*val = chip->calibscale;
45162306a36Sopenharmony_ci			*val2 = chip->ucalibscale;
45262306a36Sopenharmony_ci			ret = IIO_VAL_INT_PLUS_MICRO;
45362306a36Sopenharmony_ci		}
45462306a36Sopenharmony_ci		break;
45562306a36Sopenharmony_ci	default:
45662306a36Sopenharmony_ci		break;
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ciread_done:
46062306a36Sopenharmony_ci	mutex_unlock(&chip->lock);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	return ret;
46362306a36Sopenharmony_ci}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci#define ISL29018_LIGHT_CHANNEL {					\
46662306a36Sopenharmony_ci	.type = IIO_LIGHT,						\
46762306a36Sopenharmony_ci	.indexed = 1,							\
46862306a36Sopenharmony_ci	.channel = 0,							\
46962306a36Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |		\
47062306a36Sopenharmony_ci	BIT(IIO_CHAN_INFO_CALIBSCALE) |					\
47162306a36Sopenharmony_ci	BIT(IIO_CHAN_INFO_SCALE) |					\
47262306a36Sopenharmony_ci	BIT(IIO_CHAN_INFO_INT_TIME),					\
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci#define ISL29018_IR_CHANNEL {						\
47662306a36Sopenharmony_ci	.type = IIO_INTENSITY,						\
47762306a36Sopenharmony_ci	.modified = 1,							\
47862306a36Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),			\
47962306a36Sopenharmony_ci	.channel2 = IIO_MOD_LIGHT_IR,					\
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci#define ISL29018_PROXIMITY_CHANNEL {					\
48362306a36Sopenharmony_ci	.type = IIO_PROXIMITY,						\
48462306a36Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),			\
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic const struct iio_chan_spec isl29018_channels[] = {
48862306a36Sopenharmony_ci	ISL29018_LIGHT_CHANNEL,
48962306a36Sopenharmony_ci	ISL29018_IR_CHANNEL,
49062306a36Sopenharmony_ci	ISL29018_PROXIMITY_CHANNEL,
49162306a36Sopenharmony_ci};
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_cistatic const struct iio_chan_spec isl29023_channels[] = {
49462306a36Sopenharmony_ci	ISL29018_LIGHT_CHANNEL,
49562306a36Sopenharmony_ci	ISL29018_IR_CHANNEL,
49662306a36Sopenharmony_ci};
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_cistatic IIO_DEVICE_ATTR_RO(in_illuminance_integration_time_available, 0);
49962306a36Sopenharmony_cistatic IIO_DEVICE_ATTR_RO(in_illuminance_scale_available, 0);
50062306a36Sopenharmony_cistatic IIO_DEVICE_ATTR_RW(proximity_on_chip_ambient_infrared_suppression, 0);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci#define ISL29018_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic struct attribute *isl29018_attributes[] = {
50562306a36Sopenharmony_ci	ISL29018_DEV_ATTR(in_illuminance_scale_available),
50662306a36Sopenharmony_ci	ISL29018_DEV_ATTR(in_illuminance_integration_time_available),
50762306a36Sopenharmony_ci	ISL29018_DEV_ATTR(proximity_on_chip_ambient_infrared_suppression),
50862306a36Sopenharmony_ci	NULL
50962306a36Sopenharmony_ci};
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_cistatic struct attribute *isl29023_attributes[] = {
51262306a36Sopenharmony_ci	ISL29018_DEV_ATTR(in_illuminance_scale_available),
51362306a36Sopenharmony_ci	ISL29018_DEV_ATTR(in_illuminance_integration_time_available),
51462306a36Sopenharmony_ci	NULL
51562306a36Sopenharmony_ci};
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_cistatic const struct attribute_group isl29018_group = {
51862306a36Sopenharmony_ci	.attrs = isl29018_attributes,
51962306a36Sopenharmony_ci};
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic const struct attribute_group isl29023_group = {
52262306a36Sopenharmony_ci	.attrs = isl29023_attributes,
52362306a36Sopenharmony_ci};
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_cienum {
52662306a36Sopenharmony_ci	isl29018,
52762306a36Sopenharmony_ci	isl29023,
52862306a36Sopenharmony_ci	isl29035,
52962306a36Sopenharmony_ci};
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_cistatic int isl29018_chip_init(struct isl29018_chip *chip)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	int status;
53462306a36Sopenharmony_ci	struct device *dev = regmap_get_device(chip->regmap);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	if (chip->type == isl29035) {
53762306a36Sopenharmony_ci		unsigned int id;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci		status = regmap_read(chip->regmap, ISL29035_REG_DEVICE_ID, &id);
54062306a36Sopenharmony_ci		if (status < 0) {
54162306a36Sopenharmony_ci			dev_err(dev,
54262306a36Sopenharmony_ci				"Error reading ID register with error %d\n",
54362306a36Sopenharmony_ci				status);
54462306a36Sopenharmony_ci			return status;
54562306a36Sopenharmony_ci		}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci		id = (id & ISL29035_DEVICE_ID_MASK) >> ISL29035_DEVICE_ID_SHIFT;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci		if (id != ISL29035_DEVICE_ID)
55062306a36Sopenharmony_ci			return -ENODEV;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci		/* Clear brownout bit */
55362306a36Sopenharmony_ci		status = regmap_update_bits(chip->regmap,
55462306a36Sopenharmony_ci					    ISL29035_REG_DEVICE_ID,
55562306a36Sopenharmony_ci					    ISL29035_BOUT_MASK, 0);
55662306a36Sopenharmony_ci		if (status < 0)
55762306a36Sopenharmony_ci			return status;
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	/*
56162306a36Sopenharmony_ci	 * Code added per Intersil Application Note 1534:
56262306a36Sopenharmony_ci	 *     When VDD sinks to approximately 1.8V or below, some of
56362306a36Sopenharmony_ci	 * the part's registers may change their state. When VDD
56462306a36Sopenharmony_ci	 * recovers to 2.25V (or greater), the part may thus be in an
56562306a36Sopenharmony_ci	 * unknown mode of operation. The user can return the part to
56662306a36Sopenharmony_ci	 * a known mode of operation either by (a) setting VDD = 0V for
56762306a36Sopenharmony_ci	 * 1 second or more and then powering back up with a slew rate
56862306a36Sopenharmony_ci	 * of 0.5V/ms or greater, or (b) via I2C disable all ALS/PROX
56962306a36Sopenharmony_ci	 * conversions, clear the test registers, and then rewrite all
57062306a36Sopenharmony_ci	 * registers to the desired values.
57162306a36Sopenharmony_ci	 * ...
57262306a36Sopenharmony_ci	 * For ISL29011, ISL29018, ISL29021, ISL29023
57362306a36Sopenharmony_ci	 * 1. Write 0x00 to register 0x08 (TEST)
57462306a36Sopenharmony_ci	 * 2. Write 0x00 to register 0x00 (CMD1)
57562306a36Sopenharmony_ci	 * 3. Rewrite all registers to the desired values
57662306a36Sopenharmony_ci	 *
57762306a36Sopenharmony_ci	 * ISL29018 Data Sheet (FN6619.1, Feb 11, 2010) essentially says
57862306a36Sopenharmony_ci	 * the same thing EXCEPT the data sheet asks for a 1ms delay after
57962306a36Sopenharmony_ci	 * writing the CMD1 register.
58062306a36Sopenharmony_ci	 */
58162306a36Sopenharmony_ci	status = regmap_write(chip->regmap, ISL29018_REG_TEST, 0x0);
58262306a36Sopenharmony_ci	if (status < 0) {
58362306a36Sopenharmony_ci		dev_err(dev, "Failed to clear isl29018 TEST reg.(%d)\n",
58462306a36Sopenharmony_ci			status);
58562306a36Sopenharmony_ci		return status;
58662306a36Sopenharmony_ci	}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	/*
58962306a36Sopenharmony_ci	 * See Intersil AN1534 comments above.
59062306a36Sopenharmony_ci	 * "Operating Mode" (COMMAND1) register is reprogrammed when
59162306a36Sopenharmony_ci	 * data is read from the device.
59262306a36Sopenharmony_ci	 */
59362306a36Sopenharmony_ci	status = regmap_write(chip->regmap, ISL29018_REG_ADD_COMMAND1, 0);
59462306a36Sopenharmony_ci	if (status < 0) {
59562306a36Sopenharmony_ci		dev_err(dev, "Failed to clear isl29018 CMD1 reg.(%d)\n",
59662306a36Sopenharmony_ci			status);
59762306a36Sopenharmony_ci		return status;
59862306a36Sopenharmony_ci	}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	usleep_range(1000, 2000);	/* per data sheet, page 10 */
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	/* Set defaults */
60362306a36Sopenharmony_ci	status = isl29018_set_scale(chip, chip->scale.scale,
60462306a36Sopenharmony_ci				    chip->scale.uscale);
60562306a36Sopenharmony_ci	if (status < 0) {
60662306a36Sopenharmony_ci		dev_err(dev, "Init of isl29018 fails\n");
60762306a36Sopenharmony_ci		return status;
60862306a36Sopenharmony_ci	}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	status = isl29018_set_integration_time(chip,
61162306a36Sopenharmony_ci			isl29018_int_utimes[chip->type][chip->int_time]);
61262306a36Sopenharmony_ci	if (status < 0)
61362306a36Sopenharmony_ci		dev_err(dev, "Init of isl29018 fails\n");
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	return status;
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_cistatic const struct iio_info isl29018_info = {
61962306a36Sopenharmony_ci	.attrs = &isl29018_group,
62062306a36Sopenharmony_ci	.read_raw = isl29018_read_raw,
62162306a36Sopenharmony_ci	.write_raw = isl29018_write_raw,
62262306a36Sopenharmony_ci};
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_cistatic const struct iio_info isl29023_info = {
62562306a36Sopenharmony_ci	.attrs = &isl29023_group,
62662306a36Sopenharmony_ci	.read_raw = isl29018_read_raw,
62762306a36Sopenharmony_ci	.write_raw = isl29018_write_raw,
62862306a36Sopenharmony_ci};
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_cistatic bool isl29018_is_volatile_reg(struct device *dev, unsigned int reg)
63162306a36Sopenharmony_ci{
63262306a36Sopenharmony_ci	switch (reg) {
63362306a36Sopenharmony_ci	case ISL29018_REG_ADD_DATA_LSB:
63462306a36Sopenharmony_ci	case ISL29018_REG_ADD_DATA_MSB:
63562306a36Sopenharmony_ci	case ISL29018_REG_ADD_COMMAND1:
63662306a36Sopenharmony_ci	case ISL29018_REG_TEST:
63762306a36Sopenharmony_ci	case ISL29035_REG_DEVICE_ID:
63862306a36Sopenharmony_ci		return true;
63962306a36Sopenharmony_ci	default:
64062306a36Sopenharmony_ci		return false;
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_cistatic const struct regmap_config isl29018_regmap_config = {
64562306a36Sopenharmony_ci	.reg_bits = 8,
64662306a36Sopenharmony_ci	.val_bits = 8,
64762306a36Sopenharmony_ci	.volatile_reg = isl29018_is_volatile_reg,
64862306a36Sopenharmony_ci	.max_register = ISL29018_REG_TEST,
64962306a36Sopenharmony_ci	.num_reg_defaults_raw = ISL29018_REG_TEST + 1,
65062306a36Sopenharmony_ci	.cache_type = REGCACHE_RBTREE,
65162306a36Sopenharmony_ci};
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_cistatic const struct regmap_config isl29035_regmap_config = {
65462306a36Sopenharmony_ci	.reg_bits = 8,
65562306a36Sopenharmony_ci	.val_bits = 8,
65662306a36Sopenharmony_ci	.volatile_reg = isl29018_is_volatile_reg,
65762306a36Sopenharmony_ci	.max_register = ISL29035_REG_DEVICE_ID,
65862306a36Sopenharmony_ci	.num_reg_defaults_raw = ISL29035_REG_DEVICE_ID + 1,
65962306a36Sopenharmony_ci	.cache_type = REGCACHE_RBTREE,
66062306a36Sopenharmony_ci};
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cistruct isl29018_chip_info {
66362306a36Sopenharmony_ci	const struct iio_chan_spec *channels;
66462306a36Sopenharmony_ci	int num_channels;
66562306a36Sopenharmony_ci	const struct iio_info *indio_info;
66662306a36Sopenharmony_ci	const struct regmap_config *regmap_cfg;
66762306a36Sopenharmony_ci};
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_cistatic const struct isl29018_chip_info isl29018_chip_info_tbl[] = {
67062306a36Sopenharmony_ci	[isl29018] = {
67162306a36Sopenharmony_ci		.channels = isl29018_channels,
67262306a36Sopenharmony_ci		.num_channels = ARRAY_SIZE(isl29018_channels),
67362306a36Sopenharmony_ci		.indio_info = &isl29018_info,
67462306a36Sopenharmony_ci		.regmap_cfg = &isl29018_regmap_config,
67562306a36Sopenharmony_ci	},
67662306a36Sopenharmony_ci	[isl29023] = {
67762306a36Sopenharmony_ci		.channels = isl29023_channels,
67862306a36Sopenharmony_ci		.num_channels = ARRAY_SIZE(isl29023_channels),
67962306a36Sopenharmony_ci		.indio_info = &isl29023_info,
68062306a36Sopenharmony_ci		.regmap_cfg = &isl29018_regmap_config,
68162306a36Sopenharmony_ci	},
68262306a36Sopenharmony_ci	[isl29035] = {
68362306a36Sopenharmony_ci		.channels = isl29023_channels,
68462306a36Sopenharmony_ci		.num_channels = ARRAY_SIZE(isl29023_channels),
68562306a36Sopenharmony_ci		.indio_info = &isl29023_info,
68662306a36Sopenharmony_ci		.regmap_cfg = &isl29035_regmap_config,
68762306a36Sopenharmony_ci	},
68862306a36Sopenharmony_ci};
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_cistatic const char *isl29018_match_acpi_device(struct device *dev, int *data)
69162306a36Sopenharmony_ci{
69262306a36Sopenharmony_ci	const struct acpi_device_id *id;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	id = acpi_match_device(dev->driver->acpi_match_table, dev);
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	if (!id)
69762306a36Sopenharmony_ci		return NULL;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	*data = (int)id->driver_data;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	return dev_name(dev);
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic void isl29018_disable_regulator_action(void *_data)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	struct isl29018_chip *chip = _data;
70762306a36Sopenharmony_ci	int err;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	err = regulator_disable(chip->vcc_reg);
71062306a36Sopenharmony_ci	if (err)
71162306a36Sopenharmony_ci		pr_err("failed to disable isl29018's VCC regulator!\n");
71262306a36Sopenharmony_ci}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_cistatic int isl29018_probe(struct i2c_client *client)
71562306a36Sopenharmony_ci{
71662306a36Sopenharmony_ci	const struct i2c_device_id *id = i2c_client_get_device_id(client);
71762306a36Sopenharmony_ci	struct isl29018_chip *chip;
71862306a36Sopenharmony_ci	struct iio_dev *indio_dev;
71962306a36Sopenharmony_ci	int err;
72062306a36Sopenharmony_ci	const char *name = NULL;
72162306a36Sopenharmony_ci	int dev_id = 0;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
72462306a36Sopenharmony_ci	if (!indio_dev)
72562306a36Sopenharmony_ci		return -ENOMEM;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	chip = iio_priv(indio_dev);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	i2c_set_clientdata(client, indio_dev);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	if (id) {
73262306a36Sopenharmony_ci		name = id->name;
73362306a36Sopenharmony_ci		dev_id = id->driver_data;
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	if (ACPI_HANDLE(&client->dev))
73762306a36Sopenharmony_ci		name = isl29018_match_acpi_device(&client->dev, &dev_id);
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	mutex_init(&chip->lock);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	chip->type = dev_id;
74262306a36Sopenharmony_ci	chip->calibscale = 1;
74362306a36Sopenharmony_ci	chip->ucalibscale = 0;
74462306a36Sopenharmony_ci	chip->int_time = ISL29018_INT_TIME_16;
74562306a36Sopenharmony_ci	chip->scale = isl29018_scales[chip->int_time][0];
74662306a36Sopenharmony_ci	chip->suspended = false;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	chip->vcc_reg = devm_regulator_get(&client->dev, "vcc");
74962306a36Sopenharmony_ci	if (IS_ERR(chip->vcc_reg))
75062306a36Sopenharmony_ci		return dev_err_probe(&client->dev, PTR_ERR(chip->vcc_reg),
75162306a36Sopenharmony_ci				     "failed to get VCC regulator!\n");
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	err = regulator_enable(chip->vcc_reg);
75462306a36Sopenharmony_ci	if (err) {
75562306a36Sopenharmony_ci		dev_err(&client->dev, "failed to enable VCC regulator!\n");
75662306a36Sopenharmony_ci		return err;
75762306a36Sopenharmony_ci	}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	err = devm_add_action_or_reset(&client->dev, isl29018_disable_regulator_action,
76062306a36Sopenharmony_ci				 chip);
76162306a36Sopenharmony_ci	if (err) {
76262306a36Sopenharmony_ci		dev_err(&client->dev, "failed to setup regulator cleanup action!\n");
76362306a36Sopenharmony_ci		return err;
76462306a36Sopenharmony_ci	}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	chip->regmap = devm_regmap_init_i2c(client,
76762306a36Sopenharmony_ci				isl29018_chip_info_tbl[dev_id].regmap_cfg);
76862306a36Sopenharmony_ci	if (IS_ERR(chip->regmap)) {
76962306a36Sopenharmony_ci		err = PTR_ERR(chip->regmap);
77062306a36Sopenharmony_ci		dev_err(&client->dev, "regmap initialization fails: %d\n", err);
77162306a36Sopenharmony_ci		return err;
77262306a36Sopenharmony_ci	}
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	err = isl29018_chip_init(chip);
77562306a36Sopenharmony_ci	if (err)
77662306a36Sopenharmony_ci		return err;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	indio_dev->info = isl29018_chip_info_tbl[dev_id].indio_info;
77962306a36Sopenharmony_ci	indio_dev->channels = isl29018_chip_info_tbl[dev_id].channels;
78062306a36Sopenharmony_ci	indio_dev->num_channels = isl29018_chip_info_tbl[dev_id].num_channels;
78162306a36Sopenharmony_ci	indio_dev->name = name;
78262306a36Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	return devm_iio_device_register(&client->dev, indio_dev);
78562306a36Sopenharmony_ci}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_cistatic int isl29018_suspend(struct device *dev)
78862306a36Sopenharmony_ci{
78962306a36Sopenharmony_ci	struct isl29018_chip *chip = iio_priv(dev_get_drvdata(dev));
79062306a36Sopenharmony_ci	int ret;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	mutex_lock(&chip->lock);
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	/*
79562306a36Sopenharmony_ci	 * Since this driver uses only polling commands, we are by default in
79662306a36Sopenharmony_ci	 * auto shutdown (ie, power-down) mode.
79762306a36Sopenharmony_ci	 * So we do not have much to do here.
79862306a36Sopenharmony_ci	 */
79962306a36Sopenharmony_ci	chip->suspended = true;
80062306a36Sopenharmony_ci	ret = regulator_disable(chip->vcc_reg);
80162306a36Sopenharmony_ci	if (ret)
80262306a36Sopenharmony_ci		dev_err(dev, "failed to disable VCC regulator\n");
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	mutex_unlock(&chip->lock);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	return ret;
80762306a36Sopenharmony_ci}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_cistatic int isl29018_resume(struct device *dev)
81062306a36Sopenharmony_ci{
81162306a36Sopenharmony_ci	struct isl29018_chip *chip = iio_priv(dev_get_drvdata(dev));
81262306a36Sopenharmony_ci	int err;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	mutex_lock(&chip->lock);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	err = regulator_enable(chip->vcc_reg);
81762306a36Sopenharmony_ci	if (err) {
81862306a36Sopenharmony_ci		dev_err(dev, "failed to enable VCC regulator\n");
81962306a36Sopenharmony_ci		mutex_unlock(&chip->lock);
82062306a36Sopenharmony_ci		return err;
82162306a36Sopenharmony_ci	}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	err = isl29018_chip_init(chip);
82462306a36Sopenharmony_ci	if (!err)
82562306a36Sopenharmony_ci		chip->suspended = false;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	mutex_unlock(&chip->lock);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	return err;
83062306a36Sopenharmony_ci}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(isl29018_pm_ops, isl29018_suspend,
83362306a36Sopenharmony_ci				isl29018_resume);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci#ifdef CONFIG_ACPI
83662306a36Sopenharmony_cistatic const struct acpi_device_id isl29018_acpi_match[] = {
83762306a36Sopenharmony_ci	{"ISL29018", isl29018},
83862306a36Sopenharmony_ci	{"ISL29023", isl29023},
83962306a36Sopenharmony_ci	{"ISL29035", isl29035},
84062306a36Sopenharmony_ci	{},
84162306a36Sopenharmony_ci};
84262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, isl29018_acpi_match);
84362306a36Sopenharmony_ci#endif
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_cistatic const struct i2c_device_id isl29018_id[] = {
84662306a36Sopenharmony_ci	{"isl29018", isl29018},
84762306a36Sopenharmony_ci	{"isl29023", isl29023},
84862306a36Sopenharmony_ci	{"isl29035", isl29035},
84962306a36Sopenharmony_ci	{}
85062306a36Sopenharmony_ci};
85162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, isl29018_id);
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_cistatic const struct of_device_id isl29018_of_match[] = {
85462306a36Sopenharmony_ci	{ .compatible = "isil,isl29018", },
85562306a36Sopenharmony_ci	{ .compatible = "isil,isl29023", },
85662306a36Sopenharmony_ci	{ .compatible = "isil,isl29035", },
85762306a36Sopenharmony_ci	{ },
85862306a36Sopenharmony_ci};
85962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, isl29018_of_match);
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_cistatic struct i2c_driver isl29018_driver = {
86262306a36Sopenharmony_ci	.driver	 = {
86362306a36Sopenharmony_ci			.name = "isl29018",
86462306a36Sopenharmony_ci			.acpi_match_table = ACPI_PTR(isl29018_acpi_match),
86562306a36Sopenharmony_ci			.pm = pm_sleep_ptr(&isl29018_pm_ops),
86662306a36Sopenharmony_ci			.of_match_table = isl29018_of_match,
86762306a36Sopenharmony_ci		    },
86862306a36Sopenharmony_ci	.probe = isl29018_probe,
86962306a36Sopenharmony_ci	.id_table = isl29018_id,
87062306a36Sopenharmony_ci};
87162306a36Sopenharmony_cimodule_i2c_driver(isl29018_driver);
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ciMODULE_DESCRIPTION("ISL29018 Ambient Light Sensor driver");
87462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
875