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
max44009_read_int_time(struct max44009_data * data)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
max44009_write_int_time(struct max44009_data * data,int val,int val2)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
max44009_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)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
max44009_write_raw_get_fmt(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,long mask)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
max44009_lux_raw(u8 hi,u8 lo)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
max44009_read_lux_raw(struct max44009_data * data)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
max44009_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)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
max44009_threshold_byte_from_fraction(int integral,int fractional)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
max44009_get_thr_reg(enum iio_event_direction dir)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
max44009_write_event_value(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,enum iio_event_type type,enum iio_event_direction dir,enum iio_event_info info,int val,int val2)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
max44009_read_threshold(struct iio_dev * indio_dev,enum iio_event_direction dir)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
max44009_read_event_value(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,enum iio_event_type type,enum iio_event_direction dir,enum iio_event_info info,int * val,int * val2)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
max44009_write_event_config(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,enum iio_event_type type,enum iio_event_direction dir,int state)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
max44009_read_event_config(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,enum iio_event_type type,enum iio_event_direction dir)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
max44009_threaded_irq_handler(int irq,void * p)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
max44009_probe(struct i2c_client * client)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