xref: /linux/drivers/iio/light/max44009.c (revision 4391affa107d68b6c3e5222264e91bbc21793cf0)
16aef699aSRobert Eshleman // SPDX-License-Identifier: GPL-2.0
26aef699aSRobert Eshleman /*
36aef699aSRobert Eshleman  * max44009.c - Support for MAX44009 Ambient Light Sensor
46aef699aSRobert Eshleman  *
56aef699aSRobert Eshleman  * Copyright (c) 2019 Robert Eshleman <bobbyeshleman@gmail.com>
66aef699aSRobert Eshleman  *
76aef699aSRobert Eshleman  * Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX44009.pdf
86aef699aSRobert Eshleman  *
96aef699aSRobert Eshleman  * TODO: Support continuous mode and configuring from manual mode to
106aef699aSRobert Eshleman  *	 automatic mode.
116aef699aSRobert Eshleman  *
126aef699aSRobert Eshleman  * Default I2C address: 0x4a
136aef699aSRobert Eshleman  */
146aef699aSRobert Eshleman 
156aef699aSRobert Eshleman #include <linux/init.h>
166aef699aSRobert Eshleman #include <linux/kernel.h>
176aef699aSRobert Eshleman #include <linux/bits.h>
186aef699aSRobert Eshleman #include <linux/i2c.h>
196aef699aSRobert Eshleman #include <linux/iio/events.h>
206aef699aSRobert Eshleman #include <linux/iio/iio.h>
216aef699aSRobert Eshleman #include <linux/iio/sysfs.h>
226aef699aSRobert Eshleman #include <linux/interrupt.h>
236aef699aSRobert Eshleman #include <linux/module.h>
246aef699aSRobert Eshleman #include <linux/util_macros.h>
256aef699aSRobert Eshleman 
266aef699aSRobert Eshleman #define MAX44009_DRV_NAME "max44009"
276aef699aSRobert Eshleman 
286aef699aSRobert Eshleman /* Registers in datasheet order */
296aef699aSRobert Eshleman #define MAX44009_REG_INT_STATUS 0x0
306aef699aSRobert Eshleman #define MAX44009_REG_INT_EN 0x1
316aef699aSRobert Eshleman #define MAX44009_REG_CFG 0x2
326aef699aSRobert Eshleman #define MAX44009_REG_LUX_HI 0x3
336aef699aSRobert Eshleman #define MAX44009_REG_LUX_LO 0x4
346aef699aSRobert Eshleman #define MAX44009_REG_UPPER_THR 0x5
356aef699aSRobert Eshleman #define MAX44009_REG_LOWER_THR 0x6
366aef699aSRobert Eshleman #define MAX44009_REG_THR_TIMER 0x7
376aef699aSRobert Eshleman 
386aef699aSRobert Eshleman #define MAX44009_CFG_TIM_MASK GENMASK(2, 0)
396aef699aSRobert Eshleman #define MAX44009_CFG_MAN_MODE_MASK BIT(6)
406aef699aSRobert Eshleman 
416aef699aSRobert Eshleman /* The maximum rising threshold for the max44009 */
426aef699aSRobert Eshleman #define MAX44009_MAXIMUM_THRESHOLD 7520256
436aef699aSRobert Eshleman 
446aef699aSRobert Eshleman #define MAX44009_THRESH_EXP_MASK (0xf << 4)
456aef699aSRobert Eshleman #define MAX44009_THRESH_EXP_RSHIFT 4
466aef699aSRobert Eshleman #define MAX44009_THRESH_MANT_LSHIFT 4
476aef699aSRobert Eshleman #define MAX44009_THRESH_MANT_MASK 0xf
486aef699aSRobert Eshleman 
496aef699aSRobert Eshleman #define MAX44009_UPPER_THR_MINIMUM 15
506aef699aSRobert Eshleman 
516aef699aSRobert Eshleman /* The max44009 always scales raw readings by 0.045 and is non-configurable */
526aef699aSRobert Eshleman #define MAX44009_SCALE_NUMERATOR 45
536aef699aSRobert Eshleman #define MAX44009_SCALE_DENOMINATOR 1000
546aef699aSRobert Eshleman 
556aef699aSRobert Eshleman /* The fixed-point fractional multiplier for de-scaling threshold values */
566aef699aSRobert Eshleman #define MAX44009_FRACT_MULT 1000000
576aef699aSRobert Eshleman 
586aef699aSRobert Eshleman static const u32 max44009_int_time_ns_array[] = {
596aef699aSRobert Eshleman 	800000000,
606aef699aSRobert Eshleman 	400000000,
616aef699aSRobert Eshleman 	200000000,
626aef699aSRobert Eshleman 	100000000,
636aef699aSRobert Eshleman 	50000000, /* Manual mode only */
646aef699aSRobert Eshleman 	25000000, /* Manual mode only */
656aef699aSRobert Eshleman 	12500000, /* Manual mode only */
666aef699aSRobert Eshleman 	6250000,  /* Manual mode only */
676aef699aSRobert Eshleman };
686aef699aSRobert Eshleman 
696aef699aSRobert Eshleman static const char max44009_int_time_str[] =
706aef699aSRobert Eshleman 	"0.8 "
716aef699aSRobert Eshleman 	"0.4 "
726aef699aSRobert Eshleman 	"0.2 "
736aef699aSRobert Eshleman 	"0.1 "
746aef699aSRobert Eshleman 	"0.05 "
756aef699aSRobert Eshleman 	"0.025 "
766aef699aSRobert Eshleman 	"0.0125 "
776aef699aSRobert Eshleman 	"0.00625";
786aef699aSRobert Eshleman 
796aef699aSRobert Eshleman struct max44009_data {
806aef699aSRobert Eshleman 	struct i2c_client *client;
816aef699aSRobert Eshleman 	struct mutex lock;
826aef699aSRobert Eshleman };
836aef699aSRobert Eshleman 
846aef699aSRobert Eshleman static const struct iio_event_spec max44009_event_spec[] = {
856aef699aSRobert Eshleman 	{
866aef699aSRobert Eshleman 		.type = IIO_EV_TYPE_THRESH,
876aef699aSRobert Eshleman 		.dir = IIO_EV_DIR_RISING,
886aef699aSRobert Eshleman 		.mask_separate = BIT(IIO_EV_INFO_VALUE) |
896aef699aSRobert Eshleman 				 BIT(IIO_EV_INFO_ENABLE),
906aef699aSRobert Eshleman 	},
916aef699aSRobert Eshleman 	{
926aef699aSRobert Eshleman 		.type = IIO_EV_TYPE_THRESH,
936aef699aSRobert Eshleman 		.dir = IIO_EV_DIR_FALLING,
946aef699aSRobert Eshleman 		.mask_separate = BIT(IIO_EV_INFO_VALUE) |
956aef699aSRobert Eshleman 				 BIT(IIO_EV_INFO_ENABLE),
966aef699aSRobert Eshleman 	},
976aef699aSRobert Eshleman };
986aef699aSRobert Eshleman 
996aef699aSRobert Eshleman static const struct iio_chan_spec max44009_channels[] = {
1006aef699aSRobert Eshleman 	{
1016aef699aSRobert Eshleman 		.type = IIO_LIGHT,
1026aef699aSRobert Eshleman 		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
1036aef699aSRobert Eshleman 				      BIT(IIO_CHAN_INFO_INT_TIME),
1046aef699aSRobert Eshleman 		.event_spec = max44009_event_spec,
1056aef699aSRobert Eshleman 		.num_event_specs = ARRAY_SIZE(max44009_event_spec),
1066aef699aSRobert Eshleman 	},
1076aef699aSRobert Eshleman };
1086aef699aSRobert Eshleman 
1096aef699aSRobert Eshleman static int max44009_read_int_time(struct max44009_data *data)
1106aef699aSRobert Eshleman {
1116aef699aSRobert Eshleman 
1126aef699aSRobert Eshleman 	int ret = i2c_smbus_read_byte_data(data->client, MAX44009_REG_CFG);
1136aef699aSRobert Eshleman 
1146aef699aSRobert Eshleman 	if (ret < 0)
1156aef699aSRobert Eshleman 		return ret;
1166aef699aSRobert Eshleman 
1176aef699aSRobert Eshleman 	return max44009_int_time_ns_array[ret & MAX44009_CFG_TIM_MASK];
1186aef699aSRobert Eshleman }
1196aef699aSRobert Eshleman 
1206aef699aSRobert Eshleman static int max44009_write_int_time(struct max44009_data *data,
1216aef699aSRobert Eshleman 				   int val, int val2)
1226aef699aSRobert Eshleman {
1236aef699aSRobert Eshleman 	struct i2c_client *client = data->client;
1246aef699aSRobert Eshleman 	int ret, int_time, config;
1256aef699aSRobert Eshleman 	s64 ns;
1266aef699aSRobert Eshleman 
1276aef699aSRobert Eshleman 	ns = val * NSEC_PER_SEC + val2;
1286aef699aSRobert Eshleman 	int_time = find_closest_descending(
1296aef699aSRobert Eshleman 			ns,
1306aef699aSRobert Eshleman 			max44009_int_time_ns_array,
1316aef699aSRobert Eshleman 			ARRAY_SIZE(max44009_int_time_ns_array));
1326aef699aSRobert Eshleman 
1336aef699aSRobert Eshleman 	ret = i2c_smbus_read_byte_data(client, MAX44009_REG_CFG);
1346aef699aSRobert Eshleman 	if (ret < 0)
1356aef699aSRobert Eshleman 		return ret;
1366aef699aSRobert Eshleman 
1376aef699aSRobert Eshleman 	config = ret;
1386aef699aSRobert Eshleman 	config &= int_time;
1396aef699aSRobert Eshleman 
1406aef699aSRobert Eshleman 	/*
1416aef699aSRobert Eshleman 	 * To set the integration time, the device must also be in manual
1426aef699aSRobert Eshleman 	 * mode.
1436aef699aSRobert Eshleman 	 */
1446aef699aSRobert Eshleman 	config |= MAX44009_CFG_MAN_MODE_MASK;
1456aef699aSRobert Eshleman 
1466aef699aSRobert Eshleman 	return i2c_smbus_write_byte_data(client, MAX44009_REG_CFG, config);
1476aef699aSRobert Eshleman }
1486aef699aSRobert Eshleman 
1496aef699aSRobert Eshleman static int max44009_write_raw(struct iio_dev *indio_dev,
1506aef699aSRobert Eshleman 			      struct iio_chan_spec const *chan, int val,
1516aef699aSRobert Eshleman 			      int val2, long mask)
1526aef699aSRobert Eshleman {
1536aef699aSRobert Eshleman 	struct max44009_data *data = iio_priv(indio_dev);
1546aef699aSRobert Eshleman 	int ret;
1556aef699aSRobert Eshleman 
1566aef699aSRobert Eshleman 	if (mask == IIO_CHAN_INFO_INT_TIME && chan->type == IIO_LIGHT) {
1576aef699aSRobert Eshleman 		mutex_lock(&data->lock);
1586aef699aSRobert Eshleman 		ret = max44009_write_int_time(data, val, val2);
1596aef699aSRobert Eshleman 		mutex_unlock(&data->lock);
1606aef699aSRobert Eshleman 		return ret;
1616aef699aSRobert Eshleman 	}
1626aef699aSRobert Eshleman 	return -EINVAL;
1636aef699aSRobert Eshleman }
1646aef699aSRobert Eshleman 
1656aef699aSRobert Eshleman static int max44009_write_raw_get_fmt(struct iio_dev *indio_dev,
1666aef699aSRobert Eshleman 				      struct iio_chan_spec const *chan,
1676aef699aSRobert Eshleman 				      long mask)
1686aef699aSRobert Eshleman {
1696aef699aSRobert Eshleman 	return IIO_VAL_INT_PLUS_NANO;
1706aef699aSRobert Eshleman }
1716aef699aSRobert Eshleman 
1726aef699aSRobert Eshleman static int max44009_lux_raw(u8 hi, u8 lo)
1736aef699aSRobert Eshleman {
1746aef699aSRobert Eshleman 	int mantissa;
1756aef699aSRobert Eshleman 	int exponent;
1766aef699aSRobert Eshleman 
1776aef699aSRobert Eshleman 	/*
1786aef699aSRobert Eshleman 	 * The mantissa consists of the low nibble of the Lux High Byte
1796aef699aSRobert Eshleman 	 * and the low nibble of the Lux Low Byte.
1806aef699aSRobert Eshleman 	 */
1816aef699aSRobert Eshleman 	mantissa = ((hi & 0xf) << 4) | (lo & 0xf);
1826aef699aSRobert Eshleman 
1836aef699aSRobert Eshleman 	/* The exponent byte is just the upper nibble of the Lux High Byte */
1846aef699aSRobert Eshleman 	exponent = (hi >> 4) & 0xf;
1856aef699aSRobert Eshleman 
1866aef699aSRobert Eshleman 	/*
1876aef699aSRobert Eshleman 	 * The exponent value is base 2 to the power of the raw exponent byte.
1886aef699aSRobert Eshleman 	 */
1896aef699aSRobert Eshleman 	exponent = 1 << exponent;
1906aef699aSRobert Eshleman 
1916aef699aSRobert Eshleman 	return exponent * mantissa;
1926aef699aSRobert Eshleman }
1936aef699aSRobert Eshleman 
1946aef699aSRobert Eshleman #define MAX44009_READ_LUX_XFER_LEN (4)
1956aef699aSRobert Eshleman 
1966aef699aSRobert Eshleman static int max44009_read_lux_raw(struct max44009_data *data)
1976aef699aSRobert Eshleman {
1986aef699aSRobert Eshleman 	int ret;
1996aef699aSRobert Eshleman 	u8 hireg = MAX44009_REG_LUX_HI;
2006aef699aSRobert Eshleman 	u8 loreg = MAX44009_REG_LUX_LO;
2016aef699aSRobert Eshleman 	u8 lo = 0;
2026aef699aSRobert Eshleman 	u8 hi = 0;
2036aef699aSRobert Eshleman 
2046aef699aSRobert Eshleman 	struct i2c_msg msgs[] = {
2056aef699aSRobert Eshleman 		{
2066aef699aSRobert Eshleman 			.addr = data->client->addr,
2076aef699aSRobert Eshleman 			.flags = 0,
2086aef699aSRobert Eshleman 			.len = sizeof(hireg),
2096aef699aSRobert Eshleman 			.buf = &hireg,
2106aef699aSRobert Eshleman 		},
2116aef699aSRobert Eshleman 		{
2126aef699aSRobert Eshleman 			.addr = data->client->addr,
2136aef699aSRobert Eshleman 			.flags = I2C_M_RD,
2146aef699aSRobert Eshleman 			.len = sizeof(hi),
2156aef699aSRobert Eshleman 			.buf = &hi,
2166aef699aSRobert Eshleman 		},
2176aef699aSRobert Eshleman 		{
2186aef699aSRobert Eshleman 			.addr = data->client->addr,
2196aef699aSRobert Eshleman 			.flags = 0,
2206aef699aSRobert Eshleman 			.len = sizeof(loreg),
2216aef699aSRobert Eshleman 			.buf = &loreg,
2226aef699aSRobert Eshleman 		},
2236aef699aSRobert Eshleman 		{
2246aef699aSRobert Eshleman 			.addr = data->client->addr,
2256aef699aSRobert Eshleman 			.flags = I2C_M_RD,
2266aef699aSRobert Eshleman 			.len = sizeof(lo),
2276aef699aSRobert Eshleman 			.buf = &lo,
2286aef699aSRobert Eshleman 		}
2296aef699aSRobert Eshleman 	};
2306aef699aSRobert Eshleman 
2316aef699aSRobert Eshleman 	/*
2326aef699aSRobert Eshleman 	 * Use i2c_transfer instead of smbus read because i2c_transfer
2336aef699aSRobert Eshleman 	 * does NOT use a stop bit between address write and data read.
2346aef699aSRobert Eshleman 	 * Using a stop bit causes disjoint upper/lower byte reads and
2356aef699aSRobert Eshleman 	 * reduces accuracy.
2366aef699aSRobert Eshleman 	 */
2376aef699aSRobert Eshleman 	ret = i2c_transfer(data->client->adapter,
2386aef699aSRobert Eshleman 			   msgs, MAX44009_READ_LUX_XFER_LEN);
2396aef699aSRobert Eshleman 
2406aef699aSRobert Eshleman 	if (ret != MAX44009_READ_LUX_XFER_LEN)
2416aef699aSRobert Eshleman 		return -EIO;
2426aef699aSRobert Eshleman 
2436aef699aSRobert Eshleman 	return max44009_lux_raw(hi, lo);
2446aef699aSRobert Eshleman }
2456aef699aSRobert Eshleman 
2466aef699aSRobert Eshleman static int max44009_read_raw(struct iio_dev *indio_dev,
2476aef699aSRobert Eshleman 			     struct iio_chan_spec const *chan, int *val,
2486aef699aSRobert Eshleman 			     int *val2, long mask)
2496aef699aSRobert Eshleman {
2506aef699aSRobert Eshleman 	struct max44009_data *data = iio_priv(indio_dev);
2516aef699aSRobert Eshleman 	int lux_raw;
2526aef699aSRobert Eshleman 	int ret;
2536aef699aSRobert Eshleman 
2546aef699aSRobert Eshleman 	switch (mask) {
2556aef699aSRobert Eshleman 	case IIO_CHAN_INFO_PROCESSED:
2566aef699aSRobert Eshleman 		switch (chan->type) {
2576aef699aSRobert Eshleman 		case IIO_LIGHT:
2586aef699aSRobert Eshleman 			ret = max44009_read_lux_raw(data);
2596aef699aSRobert Eshleman 			if (ret < 0)
2606aef699aSRobert Eshleman 				return ret;
2616aef699aSRobert Eshleman 			lux_raw = ret;
2626aef699aSRobert Eshleman 
2636aef699aSRobert Eshleman 			*val = lux_raw * MAX44009_SCALE_NUMERATOR;
2646aef699aSRobert Eshleman 			*val2 = MAX44009_SCALE_DENOMINATOR;
2656aef699aSRobert Eshleman 			return IIO_VAL_FRACTIONAL;
2666aef699aSRobert Eshleman 		default:
2676aef699aSRobert Eshleman 			return -EINVAL;
2686aef699aSRobert Eshleman 		}
2696aef699aSRobert Eshleman 	case IIO_CHAN_INFO_INT_TIME:
2706aef699aSRobert Eshleman 		switch (chan->type) {
2716aef699aSRobert Eshleman 		case IIO_LIGHT:
2726aef699aSRobert Eshleman 			ret = max44009_read_int_time(data);
2736aef699aSRobert Eshleman 			if (ret < 0)
2746aef699aSRobert Eshleman 				return ret;
2756aef699aSRobert Eshleman 
2766aef699aSRobert Eshleman 			*val2 = ret;
2776aef699aSRobert Eshleman 			*val = 0;
2786aef699aSRobert Eshleman 			return IIO_VAL_INT_PLUS_NANO;
2796aef699aSRobert Eshleman 		default:
2806aef699aSRobert Eshleman 			return -EINVAL;
2816aef699aSRobert Eshleman 		}
2826aef699aSRobert Eshleman 	default:
2836aef699aSRobert Eshleman 		return -EINVAL;
2846aef699aSRobert Eshleman 	}
2856aef699aSRobert Eshleman }
2866aef699aSRobert Eshleman 
2876aef699aSRobert Eshleman static IIO_CONST_ATTR(illuminance_integration_time_available,
2886aef699aSRobert Eshleman 		      max44009_int_time_str);
2896aef699aSRobert Eshleman 
2906aef699aSRobert Eshleman static struct attribute *max44009_attributes[] = {
2916aef699aSRobert Eshleman 	&iio_const_attr_illuminance_integration_time_available.dev_attr.attr,
2926aef699aSRobert Eshleman 	NULL,
2936aef699aSRobert Eshleman };
2946aef699aSRobert Eshleman 
2956aef699aSRobert Eshleman static const struct attribute_group max44009_attribute_group = {
2966aef699aSRobert Eshleman 	.attrs = max44009_attributes,
2976aef699aSRobert Eshleman };
2986aef699aSRobert Eshleman 
2996aef699aSRobert Eshleman static int max44009_threshold_byte_from_fraction(int integral, int fractional)
3006aef699aSRobert Eshleman {
3016aef699aSRobert Eshleman 	int mantissa, exp;
3026aef699aSRobert Eshleman 
3036aef699aSRobert Eshleman 	if ((integral <= 0 && fractional <= 0) ||
3046aef699aSRobert Eshleman 	     integral > MAX44009_MAXIMUM_THRESHOLD ||
3056aef699aSRobert Eshleman 	     (integral == MAX44009_MAXIMUM_THRESHOLD && fractional != 0))
3066aef699aSRobert Eshleman 		return -EINVAL;
3076aef699aSRobert Eshleman 
3086aef699aSRobert Eshleman 	/* Reverse scaling of fixed-point integral */
3096aef699aSRobert Eshleman 	mantissa = integral * MAX44009_SCALE_DENOMINATOR;
3106aef699aSRobert Eshleman 	mantissa /= MAX44009_SCALE_NUMERATOR;
3116aef699aSRobert Eshleman 
3126aef699aSRobert Eshleman 	/* Reverse scaling of fixed-point fractional */
3136aef699aSRobert Eshleman 	mantissa += fractional / MAX44009_FRACT_MULT *
3146aef699aSRobert Eshleman 		    (MAX44009_SCALE_DENOMINATOR / MAX44009_SCALE_NUMERATOR);
3156aef699aSRobert Eshleman 
3166aef699aSRobert Eshleman 	for (exp = 0; mantissa > 0xff; exp++)
3176aef699aSRobert Eshleman 		mantissa >>= 1;
3186aef699aSRobert Eshleman 
3196aef699aSRobert Eshleman 	mantissa >>= 4;
3206aef699aSRobert Eshleman 	mantissa &= 0xf;
3216aef699aSRobert Eshleman 	exp <<= 4;
3226aef699aSRobert Eshleman 
3236aef699aSRobert Eshleman 	return exp | mantissa;
3246aef699aSRobert Eshleman }
3256aef699aSRobert Eshleman 
3266aef699aSRobert Eshleman static int max44009_get_thr_reg(enum iio_event_direction dir)
3276aef699aSRobert Eshleman {
3286aef699aSRobert Eshleman 	switch (dir) {
3296aef699aSRobert Eshleman 	case IIO_EV_DIR_RISING:
3306aef699aSRobert Eshleman 		return MAX44009_REG_UPPER_THR;
3316aef699aSRobert Eshleman 	case IIO_EV_DIR_FALLING:
3326aef699aSRobert Eshleman 		return MAX44009_REG_LOWER_THR;
3336aef699aSRobert Eshleman 	default:
3346aef699aSRobert Eshleman 		return -EINVAL;
3356aef699aSRobert Eshleman 	}
3366aef699aSRobert Eshleman }
3376aef699aSRobert Eshleman 
3386aef699aSRobert Eshleman static int max44009_write_event_value(struct iio_dev *indio_dev,
3396aef699aSRobert Eshleman 				      const struct iio_chan_spec *chan,
3406aef699aSRobert Eshleman 				      enum iio_event_type type,
3416aef699aSRobert Eshleman 				      enum iio_event_direction dir,
3426aef699aSRobert Eshleman 				      enum iio_event_info info,
3436aef699aSRobert Eshleman 				      int val, int val2)
3446aef699aSRobert Eshleman {
3456aef699aSRobert Eshleman 	struct max44009_data *data = iio_priv(indio_dev);
3466aef699aSRobert Eshleman 	int reg, threshold;
3476aef699aSRobert Eshleman 
3486aef699aSRobert Eshleman 	if (info != IIO_EV_INFO_VALUE || chan->type != IIO_LIGHT)
3496aef699aSRobert Eshleman 		return -EINVAL;
3506aef699aSRobert Eshleman 
3516aef699aSRobert Eshleman 	threshold = max44009_threshold_byte_from_fraction(val, val2);
3526aef699aSRobert Eshleman 	if (threshold < 0)
3536aef699aSRobert Eshleman 		return threshold;
3546aef699aSRobert Eshleman 
3556aef699aSRobert Eshleman 	reg = max44009_get_thr_reg(dir);
3566aef699aSRobert Eshleman 	if (reg < 0)
3576aef699aSRobert Eshleman 		return reg;
3586aef699aSRobert Eshleman 
3596aef699aSRobert Eshleman 	return i2c_smbus_write_byte_data(data->client, reg, threshold);
3606aef699aSRobert Eshleman }
3616aef699aSRobert Eshleman 
3626aef699aSRobert Eshleman static int max44009_read_threshold(struct iio_dev *indio_dev,
3636aef699aSRobert Eshleman 				   enum iio_event_direction dir)
3646aef699aSRobert Eshleman {
3656aef699aSRobert Eshleman 	struct max44009_data *data = iio_priv(indio_dev);
3666aef699aSRobert Eshleman 	int byte, reg;
3676aef699aSRobert Eshleman 	int mantissa, exponent;
3686aef699aSRobert Eshleman 
3696aef699aSRobert Eshleman 	reg = max44009_get_thr_reg(dir);
3706aef699aSRobert Eshleman 	if (reg < 0)
3716aef699aSRobert Eshleman 		return reg;
3726aef699aSRobert Eshleman 
3736aef699aSRobert Eshleman 	byte = i2c_smbus_read_byte_data(data->client, reg);
3746aef699aSRobert Eshleman 	if (byte < 0)
3756aef699aSRobert Eshleman 		return byte;
3766aef699aSRobert Eshleman 
3776aef699aSRobert Eshleman 	mantissa = byte & MAX44009_THRESH_MANT_MASK;
3786aef699aSRobert Eshleman 	mantissa <<= MAX44009_THRESH_MANT_LSHIFT;
3796aef699aSRobert Eshleman 
3806aef699aSRobert Eshleman 	/*
3816aef699aSRobert Eshleman 	 * To get the upper threshold, always adds the minimum upper threshold
3826aef699aSRobert Eshleman 	 * value to the shifted byte value (see datasheet).
3836aef699aSRobert Eshleman 	 */
3846aef699aSRobert Eshleman 	if (dir == IIO_EV_DIR_RISING)
3856aef699aSRobert Eshleman 		mantissa += MAX44009_UPPER_THR_MINIMUM;
3866aef699aSRobert Eshleman 
3876aef699aSRobert Eshleman 	/*
3886aef699aSRobert Eshleman 	 * Exponent is base 2 to the power of the threshold exponent byte
3896aef699aSRobert Eshleman 	 * value
3906aef699aSRobert Eshleman 	 */
3916aef699aSRobert Eshleman 	exponent = byte & MAX44009_THRESH_EXP_MASK;
3926aef699aSRobert Eshleman 	exponent >>= MAX44009_THRESH_EXP_RSHIFT;
3936aef699aSRobert Eshleman 
3946aef699aSRobert Eshleman 	return (1 << exponent) * mantissa;
3956aef699aSRobert Eshleman }
3966aef699aSRobert Eshleman 
3976aef699aSRobert Eshleman static int max44009_read_event_value(struct iio_dev *indio_dev,
3986aef699aSRobert Eshleman 				     const struct iio_chan_spec *chan,
3996aef699aSRobert Eshleman 				     enum iio_event_type type,
4006aef699aSRobert Eshleman 				     enum iio_event_direction dir,
4016aef699aSRobert Eshleman 				     enum iio_event_info info,
4026aef699aSRobert Eshleman 				     int *val, int *val2)
4036aef699aSRobert Eshleman {
4046aef699aSRobert Eshleman 	int ret;
4056aef699aSRobert Eshleman 	int threshold;
4066aef699aSRobert Eshleman 
4076aef699aSRobert Eshleman 	if (chan->type != IIO_LIGHT || type != IIO_EV_TYPE_THRESH)
4086aef699aSRobert Eshleman 		return -EINVAL;
4096aef699aSRobert Eshleman 
4106aef699aSRobert Eshleman 	ret = max44009_read_threshold(indio_dev, dir);
4116aef699aSRobert Eshleman 	if (ret < 0)
4126aef699aSRobert Eshleman 		return ret;
4136aef699aSRobert Eshleman 	threshold = ret;
4146aef699aSRobert Eshleman 
4156aef699aSRobert Eshleman 	*val = threshold * MAX44009_SCALE_NUMERATOR;
4166aef699aSRobert Eshleman 	*val2 = MAX44009_SCALE_DENOMINATOR;
4176aef699aSRobert Eshleman 
4186aef699aSRobert Eshleman 	return IIO_VAL_FRACTIONAL;
4196aef699aSRobert Eshleman }
4206aef699aSRobert Eshleman 
4216aef699aSRobert Eshleman static int max44009_write_event_config(struct iio_dev *indio_dev,
4226aef699aSRobert Eshleman 				       const struct iio_chan_spec *chan,
4236aef699aSRobert Eshleman 				       enum iio_event_type type,
4246aef699aSRobert Eshleman 				       enum iio_event_direction dir,
4256aef699aSRobert Eshleman 				       int state)
4266aef699aSRobert Eshleman {
4276aef699aSRobert Eshleman 	struct max44009_data *data = iio_priv(indio_dev);
4286aef699aSRobert Eshleman 	int ret;
4296aef699aSRobert Eshleman 
4306aef699aSRobert Eshleman 	if (chan->type != IIO_LIGHT || type != IIO_EV_TYPE_THRESH)
4316aef699aSRobert Eshleman 		return -EINVAL;
4326aef699aSRobert Eshleman 
4336aef699aSRobert Eshleman 	ret = i2c_smbus_write_byte_data(data->client,
4346aef699aSRobert Eshleman 					MAX44009_REG_INT_EN, state);
4356aef699aSRobert Eshleman 	if (ret < 0)
4366aef699aSRobert Eshleman 		return ret;
4376aef699aSRobert Eshleman 
4386aef699aSRobert Eshleman 	/*
4396aef699aSRobert Eshleman 	 * Set device to trigger interrupt immediately upon exceeding
4406aef699aSRobert Eshleman 	 * the threshold limit.
4416aef699aSRobert Eshleman 	 */
4426aef699aSRobert Eshleman 	return i2c_smbus_write_byte_data(data->client,
4436aef699aSRobert Eshleman 					 MAX44009_REG_THR_TIMER, 0);
4446aef699aSRobert Eshleman }
4456aef699aSRobert Eshleman 
4466aef699aSRobert Eshleman static int max44009_read_event_config(struct iio_dev *indio_dev,
4476aef699aSRobert Eshleman 				      const struct iio_chan_spec *chan,
4486aef699aSRobert Eshleman 				      enum iio_event_type type,
4496aef699aSRobert Eshleman 				      enum iio_event_direction dir)
4506aef699aSRobert Eshleman {
4516aef699aSRobert Eshleman 	struct max44009_data *data = iio_priv(indio_dev);
4526aef699aSRobert Eshleman 
4536aef699aSRobert Eshleman 	if (chan->type != IIO_LIGHT || type != IIO_EV_TYPE_THRESH)
4546aef699aSRobert Eshleman 		return -EINVAL;
4556aef699aSRobert Eshleman 
4566aef699aSRobert Eshleman 	return i2c_smbus_read_byte_data(data->client, MAX44009_REG_INT_EN);
4576aef699aSRobert Eshleman }
4586aef699aSRobert Eshleman 
4596aef699aSRobert Eshleman static const struct iio_info max44009_info = {
4606aef699aSRobert Eshleman 	.read_raw = max44009_read_raw,
4616aef699aSRobert Eshleman 	.write_raw = max44009_write_raw,
4626aef699aSRobert Eshleman 	.write_raw_get_fmt = max44009_write_raw_get_fmt,
4636aef699aSRobert Eshleman 	.read_event_value = max44009_read_event_value,
4646aef699aSRobert Eshleman 	.read_event_config = max44009_read_event_config,
4656aef699aSRobert Eshleman 	.write_event_value = max44009_write_event_value,
4666aef699aSRobert Eshleman 	.write_event_config = max44009_write_event_config,
4676aef699aSRobert Eshleman 	.attrs = &max44009_attribute_group,
4686aef699aSRobert Eshleman };
4696aef699aSRobert Eshleman 
4706aef699aSRobert Eshleman static irqreturn_t max44009_threaded_irq_handler(int irq, void *p)
4716aef699aSRobert Eshleman {
4726aef699aSRobert Eshleman 	struct iio_dev *indio_dev = p;
4736aef699aSRobert Eshleman 	struct max44009_data *data = iio_priv(indio_dev);
4746aef699aSRobert Eshleman 	int ret;
4756aef699aSRobert Eshleman 
4766aef699aSRobert Eshleman 	ret = i2c_smbus_read_byte_data(data->client, MAX44009_REG_INT_STATUS);
4776aef699aSRobert Eshleman 	if (ret) {
4786aef699aSRobert Eshleman 		iio_push_event(indio_dev,
4796aef699aSRobert Eshleman 			       IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
4806aef699aSRobert Eshleman 						    IIO_EV_TYPE_THRESH,
4816aef699aSRobert Eshleman 						    IIO_EV_DIR_EITHER),
4826aef699aSRobert Eshleman 			       iio_get_time_ns(indio_dev));
4836aef699aSRobert Eshleman 
4846aef699aSRobert Eshleman 		return IRQ_HANDLED;
4856aef699aSRobert Eshleman 	}
4866aef699aSRobert Eshleman 
4876aef699aSRobert Eshleman 	return IRQ_NONE;
4886aef699aSRobert Eshleman }
4896aef699aSRobert Eshleman 
49022cd9320SUwe Kleine-König static int max44009_probe(struct i2c_client *client)
4916aef699aSRobert Eshleman {
4926aef699aSRobert Eshleman 	struct max44009_data *data;
4936aef699aSRobert Eshleman 	struct iio_dev *indio_dev;
4946aef699aSRobert Eshleman 	int ret;
4956aef699aSRobert Eshleman 
4966aef699aSRobert Eshleman 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
4976aef699aSRobert Eshleman 	if (!indio_dev)
4986aef699aSRobert Eshleman 		return -ENOMEM;
4996aef699aSRobert Eshleman 
5006aef699aSRobert Eshleman 	data = iio_priv(indio_dev);
5016aef699aSRobert Eshleman 	i2c_set_clientdata(client, indio_dev);
5026aef699aSRobert Eshleman 	data->client = client;
5036aef699aSRobert Eshleman 	indio_dev->info = &max44009_info;
5046aef699aSRobert Eshleman 	indio_dev->modes = INDIO_DIRECT_MODE;
5056aef699aSRobert Eshleman 	indio_dev->name = MAX44009_DRV_NAME;
5066aef699aSRobert Eshleman 	indio_dev->channels = max44009_channels;
5076aef699aSRobert Eshleman 	indio_dev->num_channels = ARRAY_SIZE(max44009_channels);
5086aef699aSRobert Eshleman 	mutex_init(&data->lock);
5096aef699aSRobert Eshleman 
5106aef699aSRobert Eshleman 	/* Clear any stale interrupt bit */
5116aef699aSRobert Eshleman 	ret = i2c_smbus_read_byte_data(client, MAX44009_REG_CFG);
5126aef699aSRobert Eshleman 	if (ret < 0)
5136aef699aSRobert Eshleman 		return ret;
5146aef699aSRobert Eshleman 
5156aef699aSRobert Eshleman 	if (client->irq > 0) {
5166aef699aSRobert Eshleman 		ret = devm_request_threaded_irq(&client->dev, client->irq,
5176aef699aSRobert Eshleman 						NULL,
5186aef699aSRobert Eshleman 						max44009_threaded_irq_handler,
5196aef699aSRobert Eshleman 						IRQF_TRIGGER_FALLING |
5206aef699aSRobert Eshleman 						IRQF_ONESHOT | IRQF_SHARED,
5216aef699aSRobert Eshleman 						"max44009_event",
5226aef699aSRobert Eshleman 						indio_dev);
5236aef699aSRobert Eshleman 		if (ret < 0)
5246aef699aSRobert Eshleman 			return ret;
5256aef699aSRobert Eshleman 	}
5266aef699aSRobert Eshleman 
5276aef699aSRobert Eshleman 	return devm_iio_device_register(&client->dev, indio_dev);
5286aef699aSRobert Eshleman }
5296aef699aSRobert Eshleman 
530b29c4902SKrzysztof Kozlowski static const struct of_device_id max44009_of_match[] = {
531b29c4902SKrzysztof Kozlowski 	{ .compatible = "maxim,max44009" },
532b29c4902SKrzysztof Kozlowski 	{ }
533b29c4902SKrzysztof Kozlowski };
534b29c4902SKrzysztof Kozlowski MODULE_DEVICE_TABLE(of, max44009_of_match);
535b29c4902SKrzysztof Kozlowski 
5366aef699aSRobert Eshleman static const struct i2c_device_id max44009_id[] = {
537*4391affaSUwe Kleine-König 	{ "max44009" },
5386aef699aSRobert Eshleman 	{ }
5396aef699aSRobert Eshleman };
5406aef699aSRobert Eshleman MODULE_DEVICE_TABLE(i2c, max44009_id);
5416aef699aSRobert Eshleman 
5426aef699aSRobert Eshleman static struct i2c_driver max44009_driver = {
5436aef699aSRobert Eshleman 	.driver = {
5446aef699aSRobert Eshleman 		.name = MAX44009_DRV_NAME,
545b29c4902SKrzysztof Kozlowski 		.of_match_table = max44009_of_match,
5466aef699aSRobert Eshleman 	},
5477cf15f42SUwe Kleine-König 	.probe = max44009_probe,
5486aef699aSRobert Eshleman 	.id_table = max44009_id,
5496aef699aSRobert Eshleman };
5506aef699aSRobert Eshleman module_i2c_driver(max44009_driver);
5516aef699aSRobert Eshleman 
5526aef699aSRobert Eshleman MODULE_AUTHOR("Robert Eshleman <bobbyeshleman@gmail.com>");
5536aef699aSRobert Eshleman MODULE_LICENSE("GPL v2");
5546aef699aSRobert Eshleman MODULE_DESCRIPTION("MAX44009 ambient light sensor driver");
555