162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * max44009.c - Support for MAX44009 Ambient Light Sensor
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2019 Robert Eshleman <bobbyeshleman@gmail.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX44009.pdf
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * TODO: Support continuous mode and configuring from manual mode to
1062306a36Sopenharmony_ci *	 automatic mode.
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * Default I2C address: 0x4a
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/bits.h>
1862306a36Sopenharmony_ci#include <linux/i2c.h>
1962306a36Sopenharmony_ci#include <linux/iio/events.h>
2062306a36Sopenharmony_ci#include <linux/iio/iio.h>
2162306a36Sopenharmony_ci#include <linux/iio/sysfs.h>
2262306a36Sopenharmony_ci#include <linux/interrupt.h>
2362306a36Sopenharmony_ci#include <linux/module.h>
2462306a36Sopenharmony_ci#include <linux/util_macros.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define MAX44009_DRV_NAME "max44009"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/* Registers in datasheet order */
2962306a36Sopenharmony_ci#define MAX44009_REG_INT_STATUS 0x0
3062306a36Sopenharmony_ci#define MAX44009_REG_INT_EN 0x1
3162306a36Sopenharmony_ci#define MAX44009_REG_CFG 0x2
3262306a36Sopenharmony_ci#define MAX44009_REG_LUX_HI 0x3
3362306a36Sopenharmony_ci#define MAX44009_REG_LUX_LO 0x4
3462306a36Sopenharmony_ci#define MAX44009_REG_UPPER_THR 0x5
3562306a36Sopenharmony_ci#define MAX44009_REG_LOWER_THR 0x6
3662306a36Sopenharmony_ci#define MAX44009_REG_THR_TIMER 0x7
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define MAX44009_CFG_TIM_MASK GENMASK(2, 0)
3962306a36Sopenharmony_ci#define MAX44009_CFG_MAN_MODE_MASK BIT(6)
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/* The maximum rising threshold for the max44009 */
4262306a36Sopenharmony_ci#define MAX44009_MAXIMUM_THRESHOLD 7520256
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define MAX44009_THRESH_EXP_MASK (0xf << 4)
4562306a36Sopenharmony_ci#define MAX44009_THRESH_EXP_RSHIFT 4
4662306a36Sopenharmony_ci#define MAX44009_THRESH_MANT_LSHIFT 4
4762306a36Sopenharmony_ci#define MAX44009_THRESH_MANT_MASK 0xf
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define MAX44009_UPPER_THR_MINIMUM 15
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/* The max44009 always scales raw readings by 0.045 and is non-configurable */
5262306a36Sopenharmony_ci#define MAX44009_SCALE_NUMERATOR 45
5362306a36Sopenharmony_ci#define MAX44009_SCALE_DENOMINATOR 1000
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/* The fixed-point fractional multiplier for de-scaling threshold values */
5662306a36Sopenharmony_ci#define MAX44009_FRACT_MULT 1000000
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic const u32 max44009_int_time_ns_array[] = {
5962306a36Sopenharmony_ci	800000000,
6062306a36Sopenharmony_ci	400000000,
6162306a36Sopenharmony_ci	200000000,
6262306a36Sopenharmony_ci	100000000,
6362306a36Sopenharmony_ci	50000000, /* Manual mode only */
6462306a36Sopenharmony_ci	25000000, /* Manual mode only */
6562306a36Sopenharmony_ci	12500000, /* Manual mode only */
6662306a36Sopenharmony_ci	6250000,  /* Manual mode only */
6762306a36Sopenharmony_ci};
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic const char max44009_int_time_str[] =
7062306a36Sopenharmony_ci	"0.8 "
7162306a36Sopenharmony_ci	"0.4 "
7262306a36Sopenharmony_ci	"0.2 "
7362306a36Sopenharmony_ci	"0.1 "
7462306a36Sopenharmony_ci	"0.05 "
7562306a36Sopenharmony_ci	"0.025 "
7662306a36Sopenharmony_ci	"0.0125 "
7762306a36Sopenharmony_ci	"0.00625";
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistruct max44009_data {
8062306a36Sopenharmony_ci	struct i2c_client *client;
8162306a36Sopenharmony_ci	struct mutex lock;
8262306a36Sopenharmony_ci};
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic const struct iio_event_spec max44009_event_spec[] = {
8562306a36Sopenharmony_ci	{
8662306a36Sopenharmony_ci		.type = IIO_EV_TYPE_THRESH,
8762306a36Sopenharmony_ci		.dir = IIO_EV_DIR_RISING,
8862306a36Sopenharmony_ci		.mask_separate = BIT(IIO_EV_INFO_VALUE) |
8962306a36Sopenharmony_ci				 BIT(IIO_EV_INFO_ENABLE),
9062306a36Sopenharmony_ci	},
9162306a36Sopenharmony_ci	{
9262306a36Sopenharmony_ci		.type = IIO_EV_TYPE_THRESH,
9362306a36Sopenharmony_ci		.dir = IIO_EV_DIR_FALLING,
9462306a36Sopenharmony_ci		.mask_separate = BIT(IIO_EV_INFO_VALUE) |
9562306a36Sopenharmony_ci				 BIT(IIO_EV_INFO_ENABLE),
9662306a36Sopenharmony_ci	},
9762306a36Sopenharmony_ci};
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic const struct iio_chan_spec max44009_channels[] = {
10062306a36Sopenharmony_ci	{
10162306a36Sopenharmony_ci		.type = IIO_LIGHT,
10262306a36Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
10362306a36Sopenharmony_ci				      BIT(IIO_CHAN_INFO_INT_TIME),
10462306a36Sopenharmony_ci		.event_spec = max44009_event_spec,
10562306a36Sopenharmony_ci		.num_event_specs = ARRAY_SIZE(max44009_event_spec),
10662306a36Sopenharmony_ci	},
10762306a36Sopenharmony_ci};
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic int max44009_read_int_time(struct max44009_data *data)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	int ret = i2c_smbus_read_byte_data(data->client, MAX44009_REG_CFG);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	if (ret < 0)
11562306a36Sopenharmony_ci		return ret;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	return max44009_int_time_ns_array[ret & MAX44009_CFG_TIM_MASK];
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic int max44009_write_int_time(struct max44009_data *data,
12162306a36Sopenharmony_ci				   int val, int val2)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct i2c_client *client = data->client;
12462306a36Sopenharmony_ci	int ret, int_time, config;
12562306a36Sopenharmony_ci	s64 ns;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	ns = val * NSEC_PER_SEC + val2;
12862306a36Sopenharmony_ci	int_time = find_closest_descending(
12962306a36Sopenharmony_ci			ns,
13062306a36Sopenharmony_ci			max44009_int_time_ns_array,
13162306a36Sopenharmony_ci			ARRAY_SIZE(max44009_int_time_ns_array));
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	ret = i2c_smbus_read_byte_data(client, MAX44009_REG_CFG);
13462306a36Sopenharmony_ci	if (ret < 0)
13562306a36Sopenharmony_ci		return ret;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	config = ret;
13862306a36Sopenharmony_ci	config &= int_time;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/*
14162306a36Sopenharmony_ci	 * To set the integration time, the device must also be in manual
14262306a36Sopenharmony_ci	 * mode.
14362306a36Sopenharmony_ci	 */
14462306a36Sopenharmony_ci	config |= MAX44009_CFG_MAN_MODE_MASK;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	return i2c_smbus_write_byte_data(client, MAX44009_REG_CFG, config);
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic int max44009_write_raw(struct iio_dev *indio_dev,
15062306a36Sopenharmony_ci			      struct iio_chan_spec const *chan, int val,
15162306a36Sopenharmony_ci			      int val2, long mask)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct max44009_data *data = iio_priv(indio_dev);
15462306a36Sopenharmony_ci	int ret;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (mask == IIO_CHAN_INFO_INT_TIME && chan->type == IIO_LIGHT) {
15762306a36Sopenharmony_ci		mutex_lock(&data->lock);
15862306a36Sopenharmony_ci		ret = max44009_write_int_time(data, val, val2);
15962306a36Sopenharmony_ci		mutex_unlock(&data->lock);
16062306a36Sopenharmony_ci		return ret;
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci	return -EINVAL;
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic int max44009_write_raw_get_fmt(struct iio_dev *indio_dev,
16662306a36Sopenharmony_ci				      struct iio_chan_spec const *chan,
16762306a36Sopenharmony_ci				      long mask)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	return IIO_VAL_INT_PLUS_NANO;
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic int max44009_lux_raw(u8 hi, u8 lo)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	int mantissa;
17562306a36Sopenharmony_ci	int exponent;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	/*
17862306a36Sopenharmony_ci	 * The mantissa consists of the low nibble of the Lux High Byte
17962306a36Sopenharmony_ci	 * and the low nibble of the Lux Low Byte.
18062306a36Sopenharmony_ci	 */
18162306a36Sopenharmony_ci	mantissa = ((hi & 0xf) << 4) | (lo & 0xf);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/* The exponent byte is just the upper nibble of the Lux High Byte */
18462306a36Sopenharmony_ci	exponent = (hi >> 4) & 0xf;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	/*
18762306a36Sopenharmony_ci	 * The exponent value is base 2 to the power of the raw exponent byte.
18862306a36Sopenharmony_ci	 */
18962306a36Sopenharmony_ci	exponent = 1 << exponent;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	return exponent * mantissa;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci#define MAX44009_READ_LUX_XFER_LEN (4)
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic int max44009_read_lux_raw(struct max44009_data *data)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	int ret;
19962306a36Sopenharmony_ci	u8 hireg = MAX44009_REG_LUX_HI;
20062306a36Sopenharmony_ci	u8 loreg = MAX44009_REG_LUX_LO;
20162306a36Sopenharmony_ci	u8 lo = 0;
20262306a36Sopenharmony_ci	u8 hi = 0;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	struct i2c_msg msgs[] = {
20562306a36Sopenharmony_ci		{
20662306a36Sopenharmony_ci			.addr = data->client->addr,
20762306a36Sopenharmony_ci			.flags = 0,
20862306a36Sopenharmony_ci			.len = sizeof(hireg),
20962306a36Sopenharmony_ci			.buf = &hireg,
21062306a36Sopenharmony_ci		},
21162306a36Sopenharmony_ci		{
21262306a36Sopenharmony_ci			.addr = data->client->addr,
21362306a36Sopenharmony_ci			.flags = I2C_M_RD,
21462306a36Sopenharmony_ci			.len = sizeof(hi),
21562306a36Sopenharmony_ci			.buf = &hi,
21662306a36Sopenharmony_ci		},
21762306a36Sopenharmony_ci		{
21862306a36Sopenharmony_ci			.addr = data->client->addr,
21962306a36Sopenharmony_ci			.flags = 0,
22062306a36Sopenharmony_ci			.len = sizeof(loreg),
22162306a36Sopenharmony_ci			.buf = &loreg,
22262306a36Sopenharmony_ci		},
22362306a36Sopenharmony_ci		{
22462306a36Sopenharmony_ci			.addr = data->client->addr,
22562306a36Sopenharmony_ci			.flags = I2C_M_RD,
22662306a36Sopenharmony_ci			.len = sizeof(lo),
22762306a36Sopenharmony_ci			.buf = &lo,
22862306a36Sopenharmony_ci		}
22962306a36Sopenharmony_ci	};
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	/*
23262306a36Sopenharmony_ci	 * Use i2c_transfer instead of smbus read because i2c_transfer
23362306a36Sopenharmony_ci	 * does NOT use a stop bit between address write and data read.
23462306a36Sopenharmony_ci	 * Using a stop bit causes disjoint upper/lower byte reads and
23562306a36Sopenharmony_ci	 * reduces accuracy.
23662306a36Sopenharmony_ci	 */
23762306a36Sopenharmony_ci	ret = i2c_transfer(data->client->adapter,
23862306a36Sopenharmony_ci			   msgs, MAX44009_READ_LUX_XFER_LEN);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (ret != MAX44009_READ_LUX_XFER_LEN)
24162306a36Sopenharmony_ci		return -EIO;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	return max44009_lux_raw(hi, lo);
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic int max44009_read_raw(struct iio_dev *indio_dev,
24762306a36Sopenharmony_ci			     struct iio_chan_spec const *chan, int *val,
24862306a36Sopenharmony_ci			     int *val2, long mask)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	struct max44009_data *data = iio_priv(indio_dev);
25162306a36Sopenharmony_ci	int lux_raw;
25262306a36Sopenharmony_ci	int ret;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	switch (mask) {
25562306a36Sopenharmony_ci	case IIO_CHAN_INFO_PROCESSED:
25662306a36Sopenharmony_ci		switch (chan->type) {
25762306a36Sopenharmony_ci		case IIO_LIGHT:
25862306a36Sopenharmony_ci			ret = max44009_read_lux_raw(data);
25962306a36Sopenharmony_ci			if (ret < 0)
26062306a36Sopenharmony_ci				return ret;
26162306a36Sopenharmony_ci			lux_raw = ret;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci			*val = lux_raw * MAX44009_SCALE_NUMERATOR;
26462306a36Sopenharmony_ci			*val2 = MAX44009_SCALE_DENOMINATOR;
26562306a36Sopenharmony_ci			return IIO_VAL_FRACTIONAL;
26662306a36Sopenharmony_ci		default:
26762306a36Sopenharmony_ci			return -EINVAL;
26862306a36Sopenharmony_ci		}
26962306a36Sopenharmony_ci	case IIO_CHAN_INFO_INT_TIME:
27062306a36Sopenharmony_ci		switch (chan->type) {
27162306a36Sopenharmony_ci		case IIO_LIGHT:
27262306a36Sopenharmony_ci			ret = max44009_read_int_time(data);
27362306a36Sopenharmony_ci			if (ret < 0)
27462306a36Sopenharmony_ci				return ret;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci			*val2 = ret;
27762306a36Sopenharmony_ci			*val = 0;
27862306a36Sopenharmony_ci			return IIO_VAL_INT_PLUS_NANO;
27962306a36Sopenharmony_ci		default:
28062306a36Sopenharmony_ci			return -EINVAL;
28162306a36Sopenharmony_ci		}
28262306a36Sopenharmony_ci	default:
28362306a36Sopenharmony_ci		return -EINVAL;
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic IIO_CONST_ATTR(illuminance_integration_time_available,
28862306a36Sopenharmony_ci		      max44009_int_time_str);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic struct attribute *max44009_attributes[] = {
29162306a36Sopenharmony_ci	&iio_const_attr_illuminance_integration_time_available.dev_attr.attr,
29262306a36Sopenharmony_ci	NULL,
29362306a36Sopenharmony_ci};
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic const struct attribute_group max44009_attribute_group = {
29662306a36Sopenharmony_ci	.attrs = max44009_attributes,
29762306a36Sopenharmony_ci};
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic int max44009_threshold_byte_from_fraction(int integral, int fractional)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	int mantissa, exp;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	if ((integral <= 0 && fractional <= 0) ||
30462306a36Sopenharmony_ci	     integral > MAX44009_MAXIMUM_THRESHOLD ||
30562306a36Sopenharmony_ci	     (integral == MAX44009_MAXIMUM_THRESHOLD && fractional != 0))
30662306a36Sopenharmony_ci		return -EINVAL;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	/* Reverse scaling of fixed-point integral */
30962306a36Sopenharmony_ci	mantissa = integral * MAX44009_SCALE_DENOMINATOR;
31062306a36Sopenharmony_ci	mantissa /= MAX44009_SCALE_NUMERATOR;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	/* Reverse scaling of fixed-point fractional */
31362306a36Sopenharmony_ci	mantissa += fractional / MAX44009_FRACT_MULT *
31462306a36Sopenharmony_ci		    (MAX44009_SCALE_DENOMINATOR / MAX44009_SCALE_NUMERATOR);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	for (exp = 0; mantissa > 0xff; exp++)
31762306a36Sopenharmony_ci		mantissa >>= 1;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	mantissa >>= 4;
32062306a36Sopenharmony_ci	mantissa &= 0xf;
32162306a36Sopenharmony_ci	exp <<= 4;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	return exp | mantissa;
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic int max44009_get_thr_reg(enum iio_event_direction dir)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	switch (dir) {
32962306a36Sopenharmony_ci	case IIO_EV_DIR_RISING:
33062306a36Sopenharmony_ci		return MAX44009_REG_UPPER_THR;
33162306a36Sopenharmony_ci	case IIO_EV_DIR_FALLING:
33262306a36Sopenharmony_ci		return MAX44009_REG_LOWER_THR;
33362306a36Sopenharmony_ci	default:
33462306a36Sopenharmony_ci		return -EINVAL;
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic int max44009_write_event_value(struct iio_dev *indio_dev,
33962306a36Sopenharmony_ci				      const struct iio_chan_spec *chan,
34062306a36Sopenharmony_ci				      enum iio_event_type type,
34162306a36Sopenharmony_ci				      enum iio_event_direction dir,
34262306a36Sopenharmony_ci				      enum iio_event_info info,
34362306a36Sopenharmony_ci				      int val, int val2)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	struct max44009_data *data = iio_priv(indio_dev);
34662306a36Sopenharmony_ci	int reg, threshold;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	if (info != IIO_EV_INFO_VALUE || chan->type != IIO_LIGHT)
34962306a36Sopenharmony_ci		return -EINVAL;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	threshold = max44009_threshold_byte_from_fraction(val, val2);
35262306a36Sopenharmony_ci	if (threshold < 0)
35362306a36Sopenharmony_ci		return threshold;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	reg = max44009_get_thr_reg(dir);
35662306a36Sopenharmony_ci	if (reg < 0)
35762306a36Sopenharmony_ci		return reg;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	return i2c_smbus_write_byte_data(data->client, reg, threshold);
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic int max44009_read_threshold(struct iio_dev *indio_dev,
36362306a36Sopenharmony_ci				   enum iio_event_direction dir)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	struct max44009_data *data = iio_priv(indio_dev);
36662306a36Sopenharmony_ci	int byte, reg;
36762306a36Sopenharmony_ci	int mantissa, exponent;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	reg = max44009_get_thr_reg(dir);
37062306a36Sopenharmony_ci	if (reg < 0)
37162306a36Sopenharmony_ci		return reg;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	byte = i2c_smbus_read_byte_data(data->client, reg);
37462306a36Sopenharmony_ci	if (byte < 0)
37562306a36Sopenharmony_ci		return byte;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	mantissa = byte & MAX44009_THRESH_MANT_MASK;
37862306a36Sopenharmony_ci	mantissa <<= MAX44009_THRESH_MANT_LSHIFT;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	/*
38162306a36Sopenharmony_ci	 * To get the upper threshold, always adds the minimum upper threshold
38262306a36Sopenharmony_ci	 * value to the shifted byte value (see datasheet).
38362306a36Sopenharmony_ci	 */
38462306a36Sopenharmony_ci	if (dir == IIO_EV_DIR_RISING)
38562306a36Sopenharmony_ci		mantissa += MAX44009_UPPER_THR_MINIMUM;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	/*
38862306a36Sopenharmony_ci	 * Exponent is base 2 to the power of the threshold exponent byte
38962306a36Sopenharmony_ci	 * value
39062306a36Sopenharmony_ci	 */
39162306a36Sopenharmony_ci	exponent = byte & MAX44009_THRESH_EXP_MASK;
39262306a36Sopenharmony_ci	exponent >>= MAX44009_THRESH_EXP_RSHIFT;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	return (1 << exponent) * mantissa;
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic int max44009_read_event_value(struct iio_dev *indio_dev,
39862306a36Sopenharmony_ci				     const struct iio_chan_spec *chan,
39962306a36Sopenharmony_ci				     enum iio_event_type type,
40062306a36Sopenharmony_ci				     enum iio_event_direction dir,
40162306a36Sopenharmony_ci				     enum iio_event_info info,
40262306a36Sopenharmony_ci				     int *val, int *val2)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	int ret;
40562306a36Sopenharmony_ci	int threshold;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	if (chan->type != IIO_LIGHT || type != IIO_EV_TYPE_THRESH)
40862306a36Sopenharmony_ci		return -EINVAL;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	ret = max44009_read_threshold(indio_dev, dir);
41162306a36Sopenharmony_ci	if (ret < 0)
41262306a36Sopenharmony_ci		return ret;
41362306a36Sopenharmony_ci	threshold = ret;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	*val = threshold * MAX44009_SCALE_NUMERATOR;
41662306a36Sopenharmony_ci	*val2 = MAX44009_SCALE_DENOMINATOR;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	return IIO_VAL_FRACTIONAL;
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic int max44009_write_event_config(struct iio_dev *indio_dev,
42262306a36Sopenharmony_ci				       const struct iio_chan_spec *chan,
42362306a36Sopenharmony_ci				       enum iio_event_type type,
42462306a36Sopenharmony_ci				       enum iio_event_direction dir,
42562306a36Sopenharmony_ci				       int state)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	struct max44009_data *data = iio_priv(indio_dev);
42862306a36Sopenharmony_ci	int ret;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	if (chan->type != IIO_LIGHT || type != IIO_EV_TYPE_THRESH)
43162306a36Sopenharmony_ci		return -EINVAL;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	ret = i2c_smbus_write_byte_data(data->client,
43462306a36Sopenharmony_ci					MAX44009_REG_INT_EN, state);
43562306a36Sopenharmony_ci	if (ret < 0)
43662306a36Sopenharmony_ci		return ret;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	/*
43962306a36Sopenharmony_ci	 * Set device to trigger interrupt immediately upon exceeding
44062306a36Sopenharmony_ci	 * the threshold limit.
44162306a36Sopenharmony_ci	 */
44262306a36Sopenharmony_ci	return i2c_smbus_write_byte_data(data->client,
44362306a36Sopenharmony_ci					 MAX44009_REG_THR_TIMER, 0);
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cistatic int max44009_read_event_config(struct iio_dev *indio_dev,
44762306a36Sopenharmony_ci				      const struct iio_chan_spec *chan,
44862306a36Sopenharmony_ci				      enum iio_event_type type,
44962306a36Sopenharmony_ci				      enum iio_event_direction dir)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	struct max44009_data *data = iio_priv(indio_dev);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	if (chan->type != IIO_LIGHT || type != IIO_EV_TYPE_THRESH)
45462306a36Sopenharmony_ci		return -EINVAL;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	return i2c_smbus_read_byte_data(data->client, MAX44009_REG_INT_EN);
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cistatic const struct iio_info max44009_info = {
46062306a36Sopenharmony_ci	.read_raw = max44009_read_raw,
46162306a36Sopenharmony_ci	.write_raw = max44009_write_raw,
46262306a36Sopenharmony_ci	.write_raw_get_fmt = max44009_write_raw_get_fmt,
46362306a36Sopenharmony_ci	.read_event_value = max44009_read_event_value,
46462306a36Sopenharmony_ci	.read_event_config = max44009_read_event_config,
46562306a36Sopenharmony_ci	.write_event_value = max44009_write_event_value,
46662306a36Sopenharmony_ci	.write_event_config = max44009_write_event_config,
46762306a36Sopenharmony_ci	.attrs = &max44009_attribute_group,
46862306a36Sopenharmony_ci};
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cistatic irqreturn_t max44009_threaded_irq_handler(int irq, void *p)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	struct iio_dev *indio_dev = p;
47362306a36Sopenharmony_ci	struct max44009_data *data = iio_priv(indio_dev);
47462306a36Sopenharmony_ci	int ret;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	ret = i2c_smbus_read_byte_data(data->client, MAX44009_REG_INT_STATUS);
47762306a36Sopenharmony_ci	if (ret) {
47862306a36Sopenharmony_ci		iio_push_event(indio_dev,
47962306a36Sopenharmony_ci			       IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
48062306a36Sopenharmony_ci						    IIO_EV_TYPE_THRESH,
48162306a36Sopenharmony_ci						    IIO_EV_DIR_EITHER),
48262306a36Sopenharmony_ci			       iio_get_time_ns(indio_dev));
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci		return IRQ_HANDLED;
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	return IRQ_NONE;
48862306a36Sopenharmony_ci}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cistatic int max44009_probe(struct i2c_client *client)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	struct max44009_data *data;
49362306a36Sopenharmony_ci	struct iio_dev *indio_dev;
49462306a36Sopenharmony_ci	int ret;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
49762306a36Sopenharmony_ci	if (!indio_dev)
49862306a36Sopenharmony_ci		return -ENOMEM;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	data = iio_priv(indio_dev);
50162306a36Sopenharmony_ci	i2c_set_clientdata(client, indio_dev);
50262306a36Sopenharmony_ci	data->client = client;
50362306a36Sopenharmony_ci	indio_dev->info = &max44009_info;
50462306a36Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
50562306a36Sopenharmony_ci	indio_dev->name = MAX44009_DRV_NAME;
50662306a36Sopenharmony_ci	indio_dev->channels = max44009_channels;
50762306a36Sopenharmony_ci	indio_dev->num_channels = ARRAY_SIZE(max44009_channels);
50862306a36Sopenharmony_ci	mutex_init(&data->lock);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	/* Clear any stale interrupt bit */
51162306a36Sopenharmony_ci	ret = i2c_smbus_read_byte_data(client, MAX44009_REG_CFG);
51262306a36Sopenharmony_ci	if (ret < 0)
51362306a36Sopenharmony_ci		return ret;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	if (client->irq > 0) {
51662306a36Sopenharmony_ci		ret = devm_request_threaded_irq(&client->dev, client->irq,
51762306a36Sopenharmony_ci						NULL,
51862306a36Sopenharmony_ci						max44009_threaded_irq_handler,
51962306a36Sopenharmony_ci						IRQF_TRIGGER_FALLING |
52062306a36Sopenharmony_ci						IRQF_ONESHOT | IRQF_SHARED,
52162306a36Sopenharmony_ci						"max44009_event",
52262306a36Sopenharmony_ci						indio_dev);
52362306a36Sopenharmony_ci		if (ret < 0)
52462306a36Sopenharmony_ci			return ret;
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	return devm_iio_device_register(&client->dev, indio_dev);
52862306a36Sopenharmony_ci}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_cistatic const struct of_device_id max44009_of_match[] = {
53162306a36Sopenharmony_ci	{ .compatible = "maxim,max44009" },
53262306a36Sopenharmony_ci	{ }
53362306a36Sopenharmony_ci};
53462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, max44009_of_match);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_cistatic const struct i2c_device_id max44009_id[] = {
53762306a36Sopenharmony_ci	{ "max44009", 0 },
53862306a36Sopenharmony_ci	{ }
53962306a36Sopenharmony_ci};
54062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, max44009_id);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_cistatic struct i2c_driver max44009_driver = {
54362306a36Sopenharmony_ci	.driver = {
54462306a36Sopenharmony_ci		.name = MAX44009_DRV_NAME,
54562306a36Sopenharmony_ci		.of_match_table = max44009_of_match,
54662306a36Sopenharmony_ci	},
54762306a36Sopenharmony_ci	.probe = max44009_probe,
54862306a36Sopenharmony_ci	.id_table = max44009_id,
54962306a36Sopenharmony_ci};
55062306a36Sopenharmony_cimodule_i2c_driver(max44009_driver);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ciMODULE_AUTHOR("Robert Eshleman <bobbyeshleman@gmail.com>");
55362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
55462306a36Sopenharmony_ciMODULE_DESCRIPTION("MAX44009 ambient light sensor driver");
555