162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * lv0104cs.c: LV0104CS Ambient Light Sensor Driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2018
662306a36Sopenharmony_ci * Author: Jeff LaBundy <jeff@labundy.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * 7-bit I2C slave address: 0x13
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Link to data sheet: https://www.onsemi.com/pub/Collateral/LV0104CS-D.PDF
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/i2c.h>
1662306a36Sopenharmony_ci#include <linux/err.h>
1762306a36Sopenharmony_ci#include <linux/mutex.h>
1862306a36Sopenharmony_ci#include <linux/delay.h>
1962306a36Sopenharmony_ci#include <linux/iio/iio.h>
2062306a36Sopenharmony_ci#include <linux/iio/sysfs.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define LV0104CS_REGVAL_MEASURE		0xE0
2362306a36Sopenharmony_ci#define LV0104CS_REGVAL_SLEEP		0x00
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define LV0104CS_SCALE_0_25X		0
2662306a36Sopenharmony_ci#define LV0104CS_SCALE_1X		1
2762306a36Sopenharmony_ci#define LV0104CS_SCALE_2X		2
2862306a36Sopenharmony_ci#define LV0104CS_SCALE_8X		3
2962306a36Sopenharmony_ci#define LV0104CS_SCALE_SHIFT		3
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define LV0104CS_INTEG_12_5MS		0
3262306a36Sopenharmony_ci#define LV0104CS_INTEG_100MS		1
3362306a36Sopenharmony_ci#define LV0104CS_INTEG_200MS		2
3462306a36Sopenharmony_ci#define LV0104CS_INTEG_SHIFT		1
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define LV0104CS_CALIBSCALE_UNITY	31
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistruct lv0104cs_private {
3962306a36Sopenharmony_ci	struct i2c_client *client;
4062306a36Sopenharmony_ci	struct mutex lock;
4162306a36Sopenharmony_ci	u8 calibscale;
4262306a36Sopenharmony_ci	u8 scale;
4362306a36Sopenharmony_ci	u8 int_time;
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistruct lv0104cs_mapping {
4762306a36Sopenharmony_ci	int val;
4862306a36Sopenharmony_ci	int val2;
4962306a36Sopenharmony_ci	u8 regval;
5062306a36Sopenharmony_ci};
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic const struct lv0104cs_mapping lv0104cs_calibscales[] = {
5362306a36Sopenharmony_ci	{ 0, 666666, 0x81 },
5462306a36Sopenharmony_ci	{ 0, 800000, 0x82 },
5562306a36Sopenharmony_ci	{ 0, 857142, 0x83 },
5662306a36Sopenharmony_ci	{ 0, 888888, 0x84 },
5762306a36Sopenharmony_ci	{ 0, 909090, 0x85 },
5862306a36Sopenharmony_ci	{ 0, 923076, 0x86 },
5962306a36Sopenharmony_ci	{ 0, 933333, 0x87 },
6062306a36Sopenharmony_ci	{ 0, 941176, 0x88 },
6162306a36Sopenharmony_ci	{ 0, 947368, 0x89 },
6262306a36Sopenharmony_ci	{ 0, 952380, 0x8A },
6362306a36Sopenharmony_ci	{ 0, 956521, 0x8B },
6462306a36Sopenharmony_ci	{ 0, 960000, 0x8C },
6562306a36Sopenharmony_ci	{ 0, 962962, 0x8D },
6662306a36Sopenharmony_ci	{ 0, 965517, 0x8E },
6762306a36Sopenharmony_ci	{ 0, 967741, 0x8F },
6862306a36Sopenharmony_ci	{ 0, 969696, 0x90 },
6962306a36Sopenharmony_ci	{ 0, 971428, 0x91 },
7062306a36Sopenharmony_ci	{ 0, 972972, 0x92 },
7162306a36Sopenharmony_ci	{ 0, 974358, 0x93 },
7262306a36Sopenharmony_ci	{ 0, 975609, 0x94 },
7362306a36Sopenharmony_ci	{ 0, 976744, 0x95 },
7462306a36Sopenharmony_ci	{ 0, 977777, 0x96 },
7562306a36Sopenharmony_ci	{ 0, 978723, 0x97 },
7662306a36Sopenharmony_ci	{ 0, 979591, 0x98 },
7762306a36Sopenharmony_ci	{ 0, 980392, 0x99 },
7862306a36Sopenharmony_ci	{ 0, 981132, 0x9A },
7962306a36Sopenharmony_ci	{ 0, 981818, 0x9B },
8062306a36Sopenharmony_ci	{ 0, 982456, 0x9C },
8162306a36Sopenharmony_ci	{ 0, 983050, 0x9D },
8262306a36Sopenharmony_ci	{ 0, 983606, 0x9E },
8362306a36Sopenharmony_ci	{ 0, 984126, 0x9F },
8462306a36Sopenharmony_ci	{ 1, 0, 0x80 },
8562306a36Sopenharmony_ci	{ 1, 16129, 0xBF },
8662306a36Sopenharmony_ci	{ 1, 16666, 0xBE },
8762306a36Sopenharmony_ci	{ 1, 17241, 0xBD },
8862306a36Sopenharmony_ci	{ 1, 17857, 0xBC },
8962306a36Sopenharmony_ci	{ 1, 18518, 0xBB },
9062306a36Sopenharmony_ci	{ 1, 19230, 0xBA },
9162306a36Sopenharmony_ci	{ 1, 20000, 0xB9 },
9262306a36Sopenharmony_ci	{ 1, 20833, 0xB8 },
9362306a36Sopenharmony_ci	{ 1, 21739, 0xB7 },
9462306a36Sopenharmony_ci	{ 1, 22727, 0xB6 },
9562306a36Sopenharmony_ci	{ 1, 23809, 0xB5 },
9662306a36Sopenharmony_ci	{ 1, 24999, 0xB4 },
9762306a36Sopenharmony_ci	{ 1, 26315, 0xB3 },
9862306a36Sopenharmony_ci	{ 1, 27777, 0xB2 },
9962306a36Sopenharmony_ci	{ 1, 29411, 0xB1 },
10062306a36Sopenharmony_ci	{ 1, 31250, 0xB0 },
10162306a36Sopenharmony_ci	{ 1, 33333, 0xAF },
10262306a36Sopenharmony_ci	{ 1, 35714, 0xAE },
10362306a36Sopenharmony_ci	{ 1, 38461, 0xAD },
10462306a36Sopenharmony_ci	{ 1, 41666, 0xAC },
10562306a36Sopenharmony_ci	{ 1, 45454, 0xAB },
10662306a36Sopenharmony_ci	{ 1, 50000, 0xAA },
10762306a36Sopenharmony_ci	{ 1, 55555, 0xA9 },
10862306a36Sopenharmony_ci	{ 1, 62500, 0xA8 },
10962306a36Sopenharmony_ci	{ 1, 71428, 0xA7 },
11062306a36Sopenharmony_ci	{ 1, 83333, 0xA6 },
11162306a36Sopenharmony_ci	{ 1, 100000, 0xA5 },
11262306a36Sopenharmony_ci	{ 1, 125000, 0xA4 },
11362306a36Sopenharmony_ci	{ 1, 166666, 0xA3 },
11462306a36Sopenharmony_ci	{ 1, 250000, 0xA2 },
11562306a36Sopenharmony_ci	{ 1, 500000, 0xA1 },
11662306a36Sopenharmony_ci};
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic const struct lv0104cs_mapping lv0104cs_scales[] = {
11962306a36Sopenharmony_ci	{ 0, 250000, LV0104CS_SCALE_0_25X << LV0104CS_SCALE_SHIFT },
12062306a36Sopenharmony_ci	{ 1, 0, LV0104CS_SCALE_1X << LV0104CS_SCALE_SHIFT },
12162306a36Sopenharmony_ci	{ 2, 0, LV0104CS_SCALE_2X << LV0104CS_SCALE_SHIFT },
12262306a36Sopenharmony_ci	{ 8, 0, LV0104CS_SCALE_8X << LV0104CS_SCALE_SHIFT },
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic const struct lv0104cs_mapping lv0104cs_int_times[] = {
12662306a36Sopenharmony_ci	{ 0, 12500, LV0104CS_INTEG_12_5MS << LV0104CS_INTEG_SHIFT },
12762306a36Sopenharmony_ci	{ 0, 100000, LV0104CS_INTEG_100MS << LV0104CS_INTEG_SHIFT },
12862306a36Sopenharmony_ci	{ 0, 200000, LV0104CS_INTEG_200MS << LV0104CS_INTEG_SHIFT },
12962306a36Sopenharmony_ci};
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic int lv0104cs_write_reg(struct i2c_client *client, u8 regval)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	int ret;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	ret = i2c_master_send(client, (char *)&regval, sizeof(regval));
13662306a36Sopenharmony_ci	if (ret < 0)
13762306a36Sopenharmony_ci		return ret;
13862306a36Sopenharmony_ci	if (ret != sizeof(regval))
13962306a36Sopenharmony_ci		return -EIO;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	return 0;
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic int lv0104cs_read_adc(struct i2c_client *client, u16 *adc_output)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	__be16 regval;
14762306a36Sopenharmony_ci	int ret;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	ret = i2c_master_recv(client, (char *)&regval, sizeof(regval));
15062306a36Sopenharmony_ci	if (ret < 0)
15162306a36Sopenharmony_ci		return ret;
15262306a36Sopenharmony_ci	if (ret != sizeof(regval))
15362306a36Sopenharmony_ci		return -EIO;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	*adc_output = be16_to_cpu(regval);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	return 0;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic int lv0104cs_get_lux(struct lv0104cs_private *lv0104cs,
16162306a36Sopenharmony_ci				int *val, int *val2)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	u8 regval = LV0104CS_REGVAL_MEASURE;
16462306a36Sopenharmony_ci	u16 adc_output;
16562306a36Sopenharmony_ci	int ret;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	regval |= lv0104cs_scales[lv0104cs->scale].regval;
16862306a36Sopenharmony_ci	regval |= lv0104cs_int_times[lv0104cs->int_time].regval;
16962306a36Sopenharmony_ci	ret = lv0104cs_write_reg(lv0104cs->client, regval);
17062306a36Sopenharmony_ci	if (ret)
17162306a36Sopenharmony_ci		return ret;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/* wait for integration time to pass (with margin) */
17462306a36Sopenharmony_ci	switch (lv0104cs->int_time) {
17562306a36Sopenharmony_ci	case LV0104CS_INTEG_12_5MS:
17662306a36Sopenharmony_ci		msleep(50);
17762306a36Sopenharmony_ci		break;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	case LV0104CS_INTEG_100MS:
18062306a36Sopenharmony_ci		msleep(150);
18162306a36Sopenharmony_ci		break;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	case LV0104CS_INTEG_200MS:
18462306a36Sopenharmony_ci		msleep(250);
18562306a36Sopenharmony_ci		break;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	default:
18862306a36Sopenharmony_ci		return -EINVAL;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	ret = lv0104cs_read_adc(lv0104cs->client, &adc_output);
19262306a36Sopenharmony_ci	if (ret)
19362306a36Sopenharmony_ci		return ret;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	ret = lv0104cs_write_reg(lv0104cs->client, LV0104CS_REGVAL_SLEEP);
19662306a36Sopenharmony_ci	if (ret)
19762306a36Sopenharmony_ci		return ret;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	/* convert ADC output to lux */
20062306a36Sopenharmony_ci	switch (lv0104cs->scale) {
20162306a36Sopenharmony_ci	case LV0104CS_SCALE_0_25X:
20262306a36Sopenharmony_ci		*val = adc_output * 4;
20362306a36Sopenharmony_ci		*val2 = 0;
20462306a36Sopenharmony_ci		return 0;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	case LV0104CS_SCALE_1X:
20762306a36Sopenharmony_ci		*val = adc_output;
20862306a36Sopenharmony_ci		*val2 = 0;
20962306a36Sopenharmony_ci		return 0;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	case LV0104CS_SCALE_2X:
21262306a36Sopenharmony_ci		*val = adc_output / 2;
21362306a36Sopenharmony_ci		*val2 = (adc_output % 2) * 500000;
21462306a36Sopenharmony_ci		return 0;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	case LV0104CS_SCALE_8X:
21762306a36Sopenharmony_ci		*val = adc_output / 8;
21862306a36Sopenharmony_ci		*val2 = (adc_output % 8) * 125000;
21962306a36Sopenharmony_ci		return 0;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	default:
22262306a36Sopenharmony_ci		return -EINVAL;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic int lv0104cs_read_raw(struct iio_dev *indio_dev,
22762306a36Sopenharmony_ci				struct iio_chan_spec const *chan,
22862306a36Sopenharmony_ci				int *val, int *val2, long mask)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct lv0104cs_private *lv0104cs = iio_priv(indio_dev);
23162306a36Sopenharmony_ci	int ret;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	if (chan->type != IIO_LIGHT)
23462306a36Sopenharmony_ci		return -EINVAL;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	mutex_lock(&lv0104cs->lock);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	switch (mask) {
23962306a36Sopenharmony_ci	case IIO_CHAN_INFO_PROCESSED:
24062306a36Sopenharmony_ci		ret = lv0104cs_get_lux(lv0104cs, val, val2);
24162306a36Sopenharmony_ci		if (ret)
24262306a36Sopenharmony_ci			goto err_mutex;
24362306a36Sopenharmony_ci		ret = IIO_VAL_INT_PLUS_MICRO;
24462306a36Sopenharmony_ci		break;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	case IIO_CHAN_INFO_CALIBSCALE:
24762306a36Sopenharmony_ci		*val = lv0104cs_calibscales[lv0104cs->calibscale].val;
24862306a36Sopenharmony_ci		*val2 = lv0104cs_calibscales[lv0104cs->calibscale].val2;
24962306a36Sopenharmony_ci		ret = IIO_VAL_INT_PLUS_MICRO;
25062306a36Sopenharmony_ci		break;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
25362306a36Sopenharmony_ci		*val = lv0104cs_scales[lv0104cs->scale].val;
25462306a36Sopenharmony_ci		*val2 = lv0104cs_scales[lv0104cs->scale].val2;
25562306a36Sopenharmony_ci		ret = IIO_VAL_INT_PLUS_MICRO;
25662306a36Sopenharmony_ci		break;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	case IIO_CHAN_INFO_INT_TIME:
25962306a36Sopenharmony_ci		*val = lv0104cs_int_times[lv0104cs->int_time].val;
26062306a36Sopenharmony_ci		*val2 = lv0104cs_int_times[lv0104cs->int_time].val2;
26162306a36Sopenharmony_ci		ret = IIO_VAL_INT_PLUS_MICRO;
26262306a36Sopenharmony_ci		break;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	default:
26562306a36Sopenharmony_ci		ret = -EINVAL;
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cierr_mutex:
26962306a36Sopenharmony_ci	mutex_unlock(&lv0104cs->lock);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	return ret;
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic int lv0104cs_set_calibscale(struct lv0104cs_private *lv0104cs,
27562306a36Sopenharmony_ci				int val, int val2)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	int calibscale = val * 1000000 + val2;
27862306a36Sopenharmony_ci	int floor, ceil, mid;
27962306a36Sopenharmony_ci	int ret, i, index;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	/* round to nearest quantized calibscale (sensitivity) */
28262306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(lv0104cs_calibscales) - 1; i++) {
28362306a36Sopenharmony_ci		floor = lv0104cs_calibscales[i].val * 1000000
28462306a36Sopenharmony_ci				+ lv0104cs_calibscales[i].val2;
28562306a36Sopenharmony_ci		ceil = lv0104cs_calibscales[i + 1].val * 1000000
28662306a36Sopenharmony_ci				+ lv0104cs_calibscales[i + 1].val2;
28762306a36Sopenharmony_ci		mid = (floor + ceil) / 2;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci		/* round down */
29062306a36Sopenharmony_ci		if (calibscale >= floor && calibscale < mid) {
29162306a36Sopenharmony_ci			index = i;
29262306a36Sopenharmony_ci			break;
29362306a36Sopenharmony_ci		}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci		/* round up */
29662306a36Sopenharmony_ci		if (calibscale >= mid && calibscale <= ceil) {
29762306a36Sopenharmony_ci			index = i + 1;
29862306a36Sopenharmony_ci			break;
29962306a36Sopenharmony_ci		}
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if (i == ARRAY_SIZE(lv0104cs_calibscales) - 1)
30362306a36Sopenharmony_ci		return -EINVAL;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	mutex_lock(&lv0104cs->lock);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	/* set calibscale (sensitivity) */
30862306a36Sopenharmony_ci	ret = lv0104cs_write_reg(lv0104cs->client,
30962306a36Sopenharmony_ci			lv0104cs_calibscales[index].regval);
31062306a36Sopenharmony_ci	if (ret)
31162306a36Sopenharmony_ci		goto err_mutex;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	lv0104cs->calibscale = index;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cierr_mutex:
31662306a36Sopenharmony_ci	mutex_unlock(&lv0104cs->lock);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	return ret;
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic int lv0104cs_set_scale(struct lv0104cs_private *lv0104cs,
32262306a36Sopenharmony_ci				int val, int val2)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	int i;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	/* hard matching */
32762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(lv0104cs_scales); i++) {
32862306a36Sopenharmony_ci		if (val != lv0104cs_scales[i].val)
32962306a36Sopenharmony_ci			continue;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci		if (val2 == lv0104cs_scales[i].val2)
33262306a36Sopenharmony_ci			break;
33362306a36Sopenharmony_ci	}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	if (i == ARRAY_SIZE(lv0104cs_scales))
33662306a36Sopenharmony_ci		return -EINVAL;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	mutex_lock(&lv0104cs->lock);
33962306a36Sopenharmony_ci	lv0104cs->scale = i;
34062306a36Sopenharmony_ci	mutex_unlock(&lv0104cs->lock);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	return 0;
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic int lv0104cs_set_int_time(struct lv0104cs_private *lv0104cs,
34662306a36Sopenharmony_ci				int val, int val2)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	int i;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	/* hard matching */
35162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(lv0104cs_int_times); i++) {
35262306a36Sopenharmony_ci		if (val != lv0104cs_int_times[i].val)
35362306a36Sopenharmony_ci			continue;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci		if (val2 == lv0104cs_int_times[i].val2)
35662306a36Sopenharmony_ci			break;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (i == ARRAY_SIZE(lv0104cs_int_times))
36062306a36Sopenharmony_ci		return -EINVAL;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	mutex_lock(&lv0104cs->lock);
36362306a36Sopenharmony_ci	lv0104cs->int_time = i;
36462306a36Sopenharmony_ci	mutex_unlock(&lv0104cs->lock);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	return 0;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic int lv0104cs_write_raw(struct iio_dev *indio_dev,
37062306a36Sopenharmony_ci				struct iio_chan_spec const *chan,
37162306a36Sopenharmony_ci				int val, int val2, long mask)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	struct lv0104cs_private *lv0104cs = iio_priv(indio_dev);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	if (chan->type != IIO_LIGHT)
37662306a36Sopenharmony_ci		return -EINVAL;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	switch (mask) {
37962306a36Sopenharmony_ci	case IIO_CHAN_INFO_CALIBSCALE:
38062306a36Sopenharmony_ci		return lv0104cs_set_calibscale(lv0104cs, val, val2);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
38362306a36Sopenharmony_ci		return lv0104cs_set_scale(lv0104cs, val, val2);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	case IIO_CHAN_INFO_INT_TIME:
38662306a36Sopenharmony_ci		return lv0104cs_set_int_time(lv0104cs, val, val2);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	default:
38962306a36Sopenharmony_ci		return -EINVAL;
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic ssize_t lv0104cs_show_calibscale_avail(struct device *dev,
39462306a36Sopenharmony_ci				struct device_attribute *attr, char *buf)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	ssize_t len = 0;
39762306a36Sopenharmony_ci	int i;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(lv0104cs_calibscales); i++) {
40062306a36Sopenharmony_ci		len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ",
40162306a36Sopenharmony_ci				lv0104cs_calibscales[i].val,
40262306a36Sopenharmony_ci				lv0104cs_calibscales[i].val2);
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	buf[len - 1] = '\n';
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	return len;
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_cistatic ssize_t lv0104cs_show_scale_avail(struct device *dev,
41162306a36Sopenharmony_ci				struct device_attribute *attr, char *buf)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	ssize_t len = 0;
41462306a36Sopenharmony_ci	int i;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(lv0104cs_scales); i++) {
41762306a36Sopenharmony_ci		len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ",
41862306a36Sopenharmony_ci				lv0104cs_scales[i].val,
41962306a36Sopenharmony_ci				lv0104cs_scales[i].val2);
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	buf[len - 1] = '\n';
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	return len;
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cistatic ssize_t lv0104cs_show_int_time_avail(struct device *dev,
42862306a36Sopenharmony_ci				struct device_attribute *attr, char *buf)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	ssize_t len = 0;
43162306a36Sopenharmony_ci	int i;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(lv0104cs_int_times); i++) {
43462306a36Sopenharmony_ci		len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ",
43562306a36Sopenharmony_ci				lv0104cs_int_times[i].val,
43662306a36Sopenharmony_ci				lv0104cs_int_times[i].val2);
43762306a36Sopenharmony_ci	}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	buf[len - 1] = '\n';
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	return len;
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic IIO_DEVICE_ATTR(calibscale_available, 0444,
44562306a36Sopenharmony_ci				lv0104cs_show_calibscale_avail, NULL, 0);
44662306a36Sopenharmony_cistatic IIO_DEVICE_ATTR(scale_available, 0444,
44762306a36Sopenharmony_ci				lv0104cs_show_scale_avail, NULL, 0);
44862306a36Sopenharmony_cistatic IIO_DEV_ATTR_INT_TIME_AVAIL(lv0104cs_show_int_time_avail);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cistatic struct attribute *lv0104cs_attributes[] = {
45162306a36Sopenharmony_ci	&iio_dev_attr_calibscale_available.dev_attr.attr,
45262306a36Sopenharmony_ci	&iio_dev_attr_scale_available.dev_attr.attr,
45362306a36Sopenharmony_ci	&iio_dev_attr_integration_time_available.dev_attr.attr,
45462306a36Sopenharmony_ci	NULL
45562306a36Sopenharmony_ci};
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic const struct attribute_group lv0104cs_attribute_group = {
45862306a36Sopenharmony_ci	.attrs = lv0104cs_attributes,
45962306a36Sopenharmony_ci};
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic const struct iio_info lv0104cs_info = {
46262306a36Sopenharmony_ci	.attrs = &lv0104cs_attribute_group,
46362306a36Sopenharmony_ci	.read_raw = &lv0104cs_read_raw,
46462306a36Sopenharmony_ci	.write_raw = &lv0104cs_write_raw,
46562306a36Sopenharmony_ci};
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic const struct iio_chan_spec lv0104cs_channels[] = {
46862306a36Sopenharmony_ci	{
46962306a36Sopenharmony_ci		.type = IIO_LIGHT,
47062306a36Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
47162306a36Sopenharmony_ci				      BIT(IIO_CHAN_INFO_CALIBSCALE) |
47262306a36Sopenharmony_ci				      BIT(IIO_CHAN_INFO_SCALE) |
47362306a36Sopenharmony_ci				      BIT(IIO_CHAN_INFO_INT_TIME),
47462306a36Sopenharmony_ci	},
47562306a36Sopenharmony_ci};
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cistatic int lv0104cs_probe(struct i2c_client *client)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	struct iio_dev *indio_dev;
48062306a36Sopenharmony_ci	struct lv0104cs_private *lv0104cs;
48162306a36Sopenharmony_ci	int ret;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*lv0104cs));
48462306a36Sopenharmony_ci	if (!indio_dev)
48562306a36Sopenharmony_ci		return -ENOMEM;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	lv0104cs = iio_priv(indio_dev);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	i2c_set_clientdata(client, lv0104cs);
49062306a36Sopenharmony_ci	lv0104cs->client = client;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	mutex_init(&lv0104cs->lock);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	lv0104cs->calibscale = LV0104CS_CALIBSCALE_UNITY;
49562306a36Sopenharmony_ci	lv0104cs->scale = LV0104CS_SCALE_1X;
49662306a36Sopenharmony_ci	lv0104cs->int_time = LV0104CS_INTEG_200MS;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	ret = lv0104cs_write_reg(lv0104cs->client,
49962306a36Sopenharmony_ci			lv0104cs_calibscales[LV0104CS_CALIBSCALE_UNITY].regval);
50062306a36Sopenharmony_ci	if (ret)
50162306a36Sopenharmony_ci		return ret;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
50462306a36Sopenharmony_ci	indio_dev->channels = lv0104cs_channels;
50562306a36Sopenharmony_ci	indio_dev->num_channels = ARRAY_SIZE(lv0104cs_channels);
50662306a36Sopenharmony_ci	indio_dev->name = client->name;
50762306a36Sopenharmony_ci	indio_dev->info = &lv0104cs_info;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	return devm_iio_device_register(&client->dev, indio_dev);
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_cistatic const struct i2c_device_id lv0104cs_id[] = {
51362306a36Sopenharmony_ci	{ "lv0104cs", 0 },
51462306a36Sopenharmony_ci	{ }
51562306a36Sopenharmony_ci};
51662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, lv0104cs_id);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_cistatic struct i2c_driver lv0104cs_i2c_driver = {
51962306a36Sopenharmony_ci	.driver = {
52062306a36Sopenharmony_ci		.name	= "lv0104cs",
52162306a36Sopenharmony_ci	},
52262306a36Sopenharmony_ci	.id_table	= lv0104cs_id,
52362306a36Sopenharmony_ci	.probe		= lv0104cs_probe,
52462306a36Sopenharmony_ci};
52562306a36Sopenharmony_cimodule_i2c_driver(lv0104cs_i2c_driver);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ciMODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
52862306a36Sopenharmony_ciMODULE_DESCRIPTION("LV0104CS Ambient Light Sensor Driver");
52962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
530