xref: /openbmc/linux/drivers/iio/light/opt4001.c (revision 7cf15f42)
19a960841SStefan Windfeldt-Prytz // SPDX-License-Identifier: GPL-2.0
29a960841SStefan Windfeldt-Prytz /*
39a960841SStefan Windfeldt-Prytz  * Copyright (C) 2023 Axis Communications AB
49a960841SStefan Windfeldt-Prytz  *
59a960841SStefan Windfeldt-Prytz  * Datasheet: https://www.ti.com/lit/gpn/opt4001
69a960841SStefan Windfeldt-Prytz  *
79a960841SStefan Windfeldt-Prytz  * Device driver for the Texas Instruments OPT4001.
89a960841SStefan Windfeldt-Prytz  */
99a960841SStefan Windfeldt-Prytz 
109a960841SStefan Windfeldt-Prytz #include <linux/bitfield.h>
119a960841SStefan Windfeldt-Prytz #include <linux/i2c.h>
129a960841SStefan Windfeldt-Prytz #include <linux/iio/iio.h>
139a960841SStefan Windfeldt-Prytz #include <linux/math64.h>
149a960841SStefan Windfeldt-Prytz #include <linux/module.h>
159a960841SStefan Windfeldt-Prytz #include <linux/property.h>
169a960841SStefan Windfeldt-Prytz #include <linux/regmap.h>
179a960841SStefan Windfeldt-Prytz #include <linux/regulator/consumer.h>
189a960841SStefan Windfeldt-Prytz 
199a960841SStefan Windfeldt-Prytz /* OPT4001 register set */
209a960841SStefan Windfeldt-Prytz #define OPT4001_LIGHT1_MSB    0x00
219a960841SStefan Windfeldt-Prytz #define OPT4001_LIGHT1_LSB    0x01
229a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL          0x0A
239a960841SStefan Windfeldt-Prytz #define OPT4001_DEVICE_ID     0x11
249a960841SStefan Windfeldt-Prytz 
259a960841SStefan Windfeldt-Prytz /* OPT4001 register mask */
269a960841SStefan Windfeldt-Prytz #define OPT4001_EXPONENT_MASK    GENMASK(15, 12)
279a960841SStefan Windfeldt-Prytz #define OPT4001_MSB_MASK         GENMASK(11, 0)
289a960841SStefan Windfeldt-Prytz #define OPT4001_LSB_MASK         GENMASK(15, 8)
299a960841SStefan Windfeldt-Prytz #define OPT4001_COUNTER_MASK     GENMASK(7, 4)
309a960841SStefan Windfeldt-Prytz #define OPT4001_CRC_MASK         GENMASK(3, 0)
319a960841SStefan Windfeldt-Prytz 
329a960841SStefan Windfeldt-Prytz /* OPT4001 device id mask */
339a960841SStefan Windfeldt-Prytz #define OPT4001_DEVICE_ID_MASK   GENMASK(11, 0)
349a960841SStefan Windfeldt-Prytz 
359a960841SStefan Windfeldt-Prytz /* OPT4001 control registers mask */
369a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL_QWAKE_MASK          GENMASK(15, 15)
379a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL_RANGE_MASK          GENMASK(13, 10)
389a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL_CONV_TIME_MASK      GENMASK(9, 6)
399a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL_OPER_MODE_MASK      GENMASK(5, 4)
409a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL_LATCH_MASK          GENMASK(3, 3)
419a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL_INT_POL_MASK        GENMASK(2, 2)
429a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL_FAULT_COUNT         GENMASK(0, 1)
439a960841SStefan Windfeldt-Prytz 
449a960841SStefan Windfeldt-Prytz /* OPT4001 constants */
459a960841SStefan Windfeldt-Prytz #define OPT4001_DEVICE_ID_VAL            0x121
469a960841SStefan Windfeldt-Prytz 
479a960841SStefan Windfeldt-Prytz /* OPT4001 operating modes */
489a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL_OPER_MODE_OFF        0x0
499a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL_OPER_MODE_FORCED     0x1
509a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL_OPER_MODE_ONE_SHOT   0x2
519a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL_OPER_MODE_CONTINUOUS 0x3
529a960841SStefan Windfeldt-Prytz 
539a960841SStefan Windfeldt-Prytz /* OPT4001 conversion control register definitions */
549a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL_CONVERSION_0_6MS   0x0
559a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL_CONVERSION_1MS     0x1
569a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL_CONVERSION_1_8MS   0x2
579a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL_CONVERSION_3_4MS   0x3
589a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL_CONVERSION_6_5MS   0x4
599a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL_CONVERSION_12_7MS  0x5
609a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL_CONVERSION_25MS    0x6
619a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL_CONVERSION_50MS    0x7
629a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL_CONVERSION_100MS   0x8
639a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL_CONVERSION_200MS   0x9
649a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL_CONVERSION_400MS   0xa
659a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL_CONVERSION_800MS   0xb
669a960841SStefan Windfeldt-Prytz 
679a960841SStefan Windfeldt-Prytz /* OPT4001 scale light level range definitions */
689a960841SStefan Windfeldt-Prytz #define OPT4001_CTRL_LIGHT_SCALE_AUTO   12
699a960841SStefan Windfeldt-Prytz 
709a960841SStefan Windfeldt-Prytz /* OPT4001 default values */
719a960841SStefan Windfeldt-Prytz #define OPT4001_DEFAULT_CONVERSION_TIME OPT4001_CTRL_CONVERSION_800MS
729a960841SStefan Windfeldt-Prytz 
739a960841SStefan Windfeldt-Prytz /*
749a960841SStefan Windfeldt-Prytz  * The different packaging of OPT4001 has different constants used when calculating
759a960841SStefan Windfeldt-Prytz  * lux values.
769a960841SStefan Windfeldt-Prytz  */
779a960841SStefan Windfeldt-Prytz struct opt4001_chip_info {
789a960841SStefan Windfeldt-Prytz 	int mul;
799a960841SStefan Windfeldt-Prytz 	int div;
809a960841SStefan Windfeldt-Prytz 	const char *name;
819a960841SStefan Windfeldt-Prytz };
829a960841SStefan Windfeldt-Prytz 
839a960841SStefan Windfeldt-Prytz struct opt4001_chip {
849a960841SStefan Windfeldt-Prytz 	struct regmap *regmap;
859a960841SStefan Windfeldt-Prytz 	struct i2c_client *client;
869a960841SStefan Windfeldt-Prytz 	u8 int_time;
879a960841SStefan Windfeldt-Prytz 	const struct opt4001_chip_info *chip_info;
889a960841SStefan Windfeldt-Prytz };
899a960841SStefan Windfeldt-Prytz 
909a960841SStefan Windfeldt-Prytz static const struct opt4001_chip_info opt4001_sot_5x3_info = {
919a960841SStefan Windfeldt-Prytz 	.mul = 4375,
929a960841SStefan Windfeldt-Prytz 	.div = 10000000,
939a960841SStefan Windfeldt-Prytz 	.name = "opt4001-sot-5x3"
949a960841SStefan Windfeldt-Prytz };
959a960841SStefan Windfeldt-Prytz 
969a960841SStefan Windfeldt-Prytz static const struct opt4001_chip_info opt4001_picostar_info = {
979a960841SStefan Windfeldt-Prytz 	.mul = 3125,
989a960841SStefan Windfeldt-Prytz 	.div = 10000000,
999a960841SStefan Windfeldt-Prytz 	.name = "opt4001-picostar"
1009a960841SStefan Windfeldt-Prytz };
1019a960841SStefan Windfeldt-Prytz 
1029a960841SStefan Windfeldt-Prytz static const int opt4001_int_time_available[][2] = {
1039a960841SStefan Windfeldt-Prytz 	{ 0,    600 },
1049a960841SStefan Windfeldt-Prytz 	{ 0,   1000 },
1059a960841SStefan Windfeldt-Prytz 	{ 0,   1800 },
1069a960841SStefan Windfeldt-Prytz 	{ 0,   3400 },
1079a960841SStefan Windfeldt-Prytz 	{ 0,   6500 },
1089a960841SStefan Windfeldt-Prytz 	{ 0,  12700 },
1099a960841SStefan Windfeldt-Prytz 	{ 0,  25000 },
1109a960841SStefan Windfeldt-Prytz 	{ 0,  50000 },
1119a960841SStefan Windfeldt-Prytz 	{ 0, 100000 },
1129a960841SStefan Windfeldt-Prytz 	{ 0, 200000 },
1139a960841SStefan Windfeldt-Prytz 	{ 0, 400000 },
1149a960841SStefan Windfeldt-Prytz 	{ 0, 800000 },
1159a960841SStefan Windfeldt-Prytz };
1169a960841SStefan Windfeldt-Prytz 
1179a960841SStefan Windfeldt-Prytz /*
1189a960841SStefan Windfeldt-Prytz  * Conversion time is integration time + time to set register
1199a960841SStefan Windfeldt-Prytz  * this is used as integration time.
1209a960841SStefan Windfeldt-Prytz  */
1219a960841SStefan Windfeldt-Prytz static const int opt4001_int_time_reg[][2] = {
1229a960841SStefan Windfeldt-Prytz 	{    600,  OPT4001_CTRL_CONVERSION_0_6MS  },
1239a960841SStefan Windfeldt-Prytz 	{   1000,  OPT4001_CTRL_CONVERSION_1MS    },
1249a960841SStefan Windfeldt-Prytz 	{   1800,  OPT4001_CTRL_CONVERSION_1_8MS  },
1259a960841SStefan Windfeldt-Prytz 	{   3400,  OPT4001_CTRL_CONVERSION_3_4MS  },
1269a960841SStefan Windfeldt-Prytz 	{   6500,  OPT4001_CTRL_CONVERSION_6_5MS  },
1279a960841SStefan Windfeldt-Prytz 	{  12700,  OPT4001_CTRL_CONVERSION_12_7MS },
1289a960841SStefan Windfeldt-Prytz 	{  25000,  OPT4001_CTRL_CONVERSION_25MS   },
1299a960841SStefan Windfeldt-Prytz 	{  50000,  OPT4001_CTRL_CONVERSION_50MS   },
1309a960841SStefan Windfeldt-Prytz 	{ 100000,  OPT4001_CTRL_CONVERSION_100MS  },
1319a960841SStefan Windfeldt-Prytz 	{ 200000,  OPT4001_CTRL_CONVERSION_200MS  },
1329a960841SStefan Windfeldt-Prytz 	{ 400000,  OPT4001_CTRL_CONVERSION_400MS  },
1339a960841SStefan Windfeldt-Prytz 	{ 800000,  OPT4001_CTRL_CONVERSION_800MS  },
1349a960841SStefan Windfeldt-Prytz };
1359a960841SStefan Windfeldt-Prytz 
opt4001_als_time_to_index(const u32 als_integration_time)1369a960841SStefan Windfeldt-Prytz static int opt4001_als_time_to_index(const u32 als_integration_time)
1379a960841SStefan Windfeldt-Prytz {
1389a960841SStefan Windfeldt-Prytz 	int i;
1399a960841SStefan Windfeldt-Prytz 
1409a960841SStefan Windfeldt-Prytz 	for (i = 0; i < ARRAY_SIZE(opt4001_int_time_available); i++) {
1419a960841SStefan Windfeldt-Prytz 		if (als_integration_time == opt4001_int_time_available[i][1])
1429a960841SStefan Windfeldt-Prytz 			return i;
1439a960841SStefan Windfeldt-Prytz 	}
1449a960841SStefan Windfeldt-Prytz 
1459a960841SStefan Windfeldt-Prytz 	return -EINVAL;
1469a960841SStefan Windfeldt-Prytz }
1479a960841SStefan Windfeldt-Prytz 
opt4001_calculate_crc(u8 exp,u32 mantissa,u8 count)1489a960841SStefan Windfeldt-Prytz static u8 opt4001_calculate_crc(u8 exp, u32 mantissa, u8 count)
1499a960841SStefan Windfeldt-Prytz {
1509a960841SStefan Windfeldt-Prytz 	u8 crc;
1519a960841SStefan Windfeldt-Prytz 
1529a960841SStefan Windfeldt-Prytz 	crc = (hweight32(mantissa) + hweight32(exp) + hweight32(count)) % 2;
1539a960841SStefan Windfeldt-Prytz 	crc |= ((hweight32(mantissa & 0xAAAAA) + hweight32(exp & 0xA)
1549a960841SStefan Windfeldt-Prytz 		 + hweight32(count & 0xA)) % 2) << 1;
1559a960841SStefan Windfeldt-Prytz 	crc |= ((hweight32(mantissa & 0x88888) + hweight32(exp & 0x8)
1569a960841SStefan Windfeldt-Prytz 		 + hweight32(count & 0x8)) % 2) << 2;
1579a960841SStefan Windfeldt-Prytz 	crc |= (hweight32(mantissa & 0x80808) % 2) << 3;
1589a960841SStefan Windfeldt-Prytz 
1599a960841SStefan Windfeldt-Prytz 	return crc;
1609a960841SStefan Windfeldt-Prytz }
1619a960841SStefan Windfeldt-Prytz 
opt4001_read_lux_value(struct iio_dev * indio_dev,int * val,int * val2)1629a960841SStefan Windfeldt-Prytz static int opt4001_read_lux_value(struct iio_dev *indio_dev,
1639a960841SStefan Windfeldt-Prytz 				  int *val, int *val2)
1649a960841SStefan Windfeldt-Prytz {
1659a960841SStefan Windfeldt-Prytz 	struct opt4001_chip *chip = iio_priv(indio_dev);
1669a960841SStefan Windfeldt-Prytz 	struct device *dev = &chip->client->dev;
1679a960841SStefan Windfeldt-Prytz 	unsigned int light1;
1689a960841SStefan Windfeldt-Prytz 	unsigned int light2;
1699a960841SStefan Windfeldt-Prytz 	u16 msb;
1709a960841SStefan Windfeldt-Prytz 	u16 lsb;
1719a960841SStefan Windfeldt-Prytz 	u8 exp;
1729a960841SStefan Windfeldt-Prytz 	u8 count;
1739a960841SStefan Windfeldt-Prytz 	u8 crc;
1749a960841SStefan Windfeldt-Prytz 	u8 calc_crc;
1759a960841SStefan Windfeldt-Prytz 	u64 lux_raw;
1769a960841SStefan Windfeldt-Prytz 	int ret;
1779a960841SStefan Windfeldt-Prytz 
1789a960841SStefan Windfeldt-Prytz 	ret = regmap_read(chip->regmap, OPT4001_LIGHT1_MSB, &light1);
1799a960841SStefan Windfeldt-Prytz 	if (ret < 0) {
1809a960841SStefan Windfeldt-Prytz 		dev_err(dev, "Failed to read data bytes");
1819a960841SStefan Windfeldt-Prytz 		return ret;
1829a960841SStefan Windfeldt-Prytz 	}
1839a960841SStefan Windfeldt-Prytz 
1849a960841SStefan Windfeldt-Prytz 	ret = regmap_read(chip->regmap, OPT4001_LIGHT1_LSB, &light2);
1859a960841SStefan Windfeldt-Prytz 	if (ret < 0) {
1869a960841SStefan Windfeldt-Prytz 		dev_err(dev, "Failed to read data bytes");
1879a960841SStefan Windfeldt-Prytz 		return ret;
1889a960841SStefan Windfeldt-Prytz 	}
1899a960841SStefan Windfeldt-Prytz 
1909a960841SStefan Windfeldt-Prytz 	count = FIELD_GET(OPT4001_COUNTER_MASK, light2);
1919a960841SStefan Windfeldt-Prytz 	exp = FIELD_GET(OPT4001_EXPONENT_MASK, light1);
1929a960841SStefan Windfeldt-Prytz 	crc = FIELD_GET(OPT4001_CRC_MASK, light2);
1939a960841SStefan Windfeldt-Prytz 	msb = FIELD_GET(OPT4001_MSB_MASK, light1);
1949a960841SStefan Windfeldt-Prytz 	lsb = FIELD_GET(OPT4001_LSB_MASK, light2);
1959a960841SStefan Windfeldt-Prytz 	lux_raw = (msb << 8) + lsb;
1969a960841SStefan Windfeldt-Prytz 	calc_crc = opt4001_calculate_crc(exp, lux_raw, count);
1979a960841SStefan Windfeldt-Prytz 	if (calc_crc != crc)
1989a960841SStefan Windfeldt-Prytz 		return -EIO;
1999a960841SStefan Windfeldt-Prytz 
2009a960841SStefan Windfeldt-Prytz 	lux_raw = lux_raw << exp;
2019a960841SStefan Windfeldt-Prytz 	lux_raw = lux_raw * chip->chip_info->mul;
2029a960841SStefan Windfeldt-Prytz 	*val = div_u64_rem(lux_raw, chip->chip_info->div, val2);
2039a960841SStefan Windfeldt-Prytz 	*val2 = *val2 * 100;
2049a960841SStefan Windfeldt-Prytz 
2059a960841SStefan Windfeldt-Prytz 	return IIO_VAL_INT_PLUS_NANO;
2069a960841SStefan Windfeldt-Prytz }
2079a960841SStefan Windfeldt-Prytz 
opt4001_set_conf(struct opt4001_chip * chip)2089a960841SStefan Windfeldt-Prytz static int opt4001_set_conf(struct opt4001_chip *chip)
2099a960841SStefan Windfeldt-Prytz {
2109a960841SStefan Windfeldt-Prytz 	struct device *dev = &chip->client->dev;
2119a960841SStefan Windfeldt-Prytz 	u16 reg;
2129a960841SStefan Windfeldt-Prytz 	int ret;
2139a960841SStefan Windfeldt-Prytz 
2149a960841SStefan Windfeldt-Prytz 	reg = FIELD_PREP(OPT4001_CTRL_RANGE_MASK, OPT4001_CTRL_LIGHT_SCALE_AUTO);
2159a960841SStefan Windfeldt-Prytz 	reg |= FIELD_PREP(OPT4001_CTRL_CONV_TIME_MASK, chip->int_time);
2169a960841SStefan Windfeldt-Prytz 	reg |= FIELD_PREP(OPT4001_CTRL_OPER_MODE_MASK, OPT4001_CTRL_OPER_MODE_CONTINUOUS);
2179a960841SStefan Windfeldt-Prytz 
2189a960841SStefan Windfeldt-Prytz 	ret = regmap_write(chip->regmap, OPT4001_CTRL, reg);
2199a960841SStefan Windfeldt-Prytz 	if (ret)
2209a960841SStefan Windfeldt-Prytz 		dev_err(dev, "Failed to set configuration\n");
2219a960841SStefan Windfeldt-Prytz 
2229a960841SStefan Windfeldt-Prytz 	return ret;
2239a960841SStefan Windfeldt-Prytz }
2249a960841SStefan Windfeldt-Prytz 
opt4001_power_down(struct opt4001_chip * chip)2259a960841SStefan Windfeldt-Prytz static int opt4001_power_down(struct opt4001_chip *chip)
2269a960841SStefan Windfeldt-Prytz {
2279a960841SStefan Windfeldt-Prytz 	struct device *dev = &chip->client->dev;
2289a960841SStefan Windfeldt-Prytz 	int ret;
2299a960841SStefan Windfeldt-Prytz 	unsigned int reg;
2309a960841SStefan Windfeldt-Prytz 
2319a960841SStefan Windfeldt-Prytz 	ret = regmap_read(chip->regmap, OPT4001_DEVICE_ID, &reg);
2329a960841SStefan Windfeldt-Prytz 	if (ret) {
2339a960841SStefan Windfeldt-Prytz 		dev_err(dev, "Failed to read configuration\n");
2349a960841SStefan Windfeldt-Prytz 		return ret;
2359a960841SStefan Windfeldt-Prytz 	}
2369a960841SStefan Windfeldt-Prytz 
2379a960841SStefan Windfeldt-Prytz 	/* MODE_OFF is 0x0 so just set bits to 0 */
2389a960841SStefan Windfeldt-Prytz 	reg &= ~OPT4001_CTRL_OPER_MODE_MASK;
2399a960841SStefan Windfeldt-Prytz 
2409a960841SStefan Windfeldt-Prytz 	ret = regmap_write(chip->regmap, OPT4001_CTRL, reg);
2419a960841SStefan Windfeldt-Prytz 	if (ret)
2429a960841SStefan Windfeldt-Prytz 		dev_err(dev, "Failed to set configuration to power down\n");
2439a960841SStefan Windfeldt-Prytz 
2449a960841SStefan Windfeldt-Prytz 	return ret;
2459a960841SStefan Windfeldt-Prytz }
2469a960841SStefan Windfeldt-Prytz 
opt4001_chip_off_action(void * data)2479a960841SStefan Windfeldt-Prytz static void opt4001_chip_off_action(void *data)
2489a960841SStefan Windfeldt-Prytz {
2499a960841SStefan Windfeldt-Prytz 	struct opt4001_chip *chip = data;
2509a960841SStefan Windfeldt-Prytz 
2519a960841SStefan Windfeldt-Prytz 	opt4001_power_down(chip);
2529a960841SStefan Windfeldt-Prytz }
2539a960841SStefan Windfeldt-Prytz 
2549a960841SStefan Windfeldt-Prytz static const struct iio_chan_spec opt4001_channels[] = {
2559a960841SStefan Windfeldt-Prytz 	{
2569a960841SStefan Windfeldt-Prytz 		.type = IIO_LIGHT,
2579a960841SStefan Windfeldt-Prytz 		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
2589a960841SStefan Windfeldt-Prytz 		.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
2599a960841SStefan Windfeldt-Prytz 		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME)
2609a960841SStefan Windfeldt-Prytz 	},
2619a960841SStefan Windfeldt-Prytz };
2629a960841SStefan Windfeldt-Prytz 
opt4001_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)2639a960841SStefan Windfeldt-Prytz static int opt4001_read_raw(struct iio_dev *indio_dev,
2649a960841SStefan Windfeldt-Prytz 			    struct iio_chan_spec const *chan,
2659a960841SStefan Windfeldt-Prytz 			    int *val, int *val2, long mask)
2669a960841SStefan Windfeldt-Prytz {
2679a960841SStefan Windfeldt-Prytz 	struct opt4001_chip *chip = iio_priv(indio_dev);
2689a960841SStefan Windfeldt-Prytz 
2699a960841SStefan Windfeldt-Prytz 	switch (mask) {
2709a960841SStefan Windfeldt-Prytz 	case IIO_CHAN_INFO_PROCESSED:
2719a960841SStefan Windfeldt-Prytz 		return opt4001_read_lux_value(indio_dev, val, val2);
2729a960841SStefan Windfeldt-Prytz 	case IIO_CHAN_INFO_INT_TIME:
2739a960841SStefan Windfeldt-Prytz 		*val = 0;
2749a960841SStefan Windfeldt-Prytz 		*val2 = opt4001_int_time_reg[chip->int_time][0];
2759a960841SStefan Windfeldt-Prytz 		return IIO_VAL_INT_PLUS_MICRO;
2769a960841SStefan Windfeldt-Prytz 	default:
2779a960841SStefan Windfeldt-Prytz 		return -EINVAL;
2789a960841SStefan Windfeldt-Prytz 	}
2799a960841SStefan Windfeldt-Prytz }
2809a960841SStefan Windfeldt-Prytz 
opt4001_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)2819a960841SStefan Windfeldt-Prytz static int opt4001_write_raw(struct iio_dev *indio_dev,
2829a960841SStefan Windfeldt-Prytz 			     struct iio_chan_spec const *chan,
2839a960841SStefan Windfeldt-Prytz 			     int val, int val2, long mask)
2849a960841SStefan Windfeldt-Prytz {
2859a960841SStefan Windfeldt-Prytz 	struct opt4001_chip *chip = iio_priv(indio_dev);
2869a960841SStefan Windfeldt-Prytz 	int int_time;
2879a960841SStefan Windfeldt-Prytz 
2889a960841SStefan Windfeldt-Prytz 	switch (mask) {
2899a960841SStefan Windfeldt-Prytz 	case IIO_CHAN_INFO_INT_TIME:
2909a960841SStefan Windfeldt-Prytz 		int_time = opt4001_als_time_to_index(val2);
2919a960841SStefan Windfeldt-Prytz 		if (int_time < 0)
2929a960841SStefan Windfeldt-Prytz 			return int_time;
2939a960841SStefan Windfeldt-Prytz 		chip->int_time = int_time;
2949a960841SStefan Windfeldt-Prytz 		return opt4001_set_conf(chip);
2959a960841SStefan Windfeldt-Prytz 	default:
2969a960841SStefan Windfeldt-Prytz 		return -EINVAL;
2979a960841SStefan Windfeldt-Prytz 	}
2989a960841SStefan Windfeldt-Prytz }
2999a960841SStefan Windfeldt-Prytz 
opt4001_read_available(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,const int ** vals,int * type,int * length,long mask)3009a960841SStefan Windfeldt-Prytz static int opt4001_read_available(struct iio_dev *indio_dev,
3019a960841SStefan Windfeldt-Prytz 				  struct iio_chan_spec const *chan,
3029a960841SStefan Windfeldt-Prytz 				  const int **vals, int *type, int *length,
3039a960841SStefan Windfeldt-Prytz 				  long mask)
3049a960841SStefan Windfeldt-Prytz {
3059a960841SStefan Windfeldt-Prytz 	switch (mask) {
3069a960841SStefan Windfeldt-Prytz 	case IIO_CHAN_INFO_INT_TIME:
3079a960841SStefan Windfeldt-Prytz 		*length = ARRAY_SIZE(opt4001_int_time_available) * 2;
3089a960841SStefan Windfeldt-Prytz 		*vals = (const int *)opt4001_int_time_available;
3099a960841SStefan Windfeldt-Prytz 		*type = IIO_VAL_INT_PLUS_MICRO;
3109a960841SStefan Windfeldt-Prytz 		return IIO_AVAIL_LIST;
3119a960841SStefan Windfeldt-Prytz 
3129a960841SStefan Windfeldt-Prytz 	default:
3139a960841SStefan Windfeldt-Prytz 		return -EINVAL;
3149a960841SStefan Windfeldt-Prytz 	}
3159a960841SStefan Windfeldt-Prytz }
3169a960841SStefan Windfeldt-Prytz 
3179a960841SStefan Windfeldt-Prytz static const struct iio_info opt4001_info_no_irq = {
3189a960841SStefan Windfeldt-Prytz 	.read_raw = opt4001_read_raw,
3199a960841SStefan Windfeldt-Prytz 	.write_raw = opt4001_write_raw,
3209a960841SStefan Windfeldt-Prytz 	.read_avail = opt4001_read_available,
3219a960841SStefan Windfeldt-Prytz };
3229a960841SStefan Windfeldt-Prytz 
opt4001_load_defaults(struct opt4001_chip * chip)3239a960841SStefan Windfeldt-Prytz static int opt4001_load_defaults(struct opt4001_chip *chip)
3249a960841SStefan Windfeldt-Prytz {
3259a960841SStefan Windfeldt-Prytz 	chip->int_time = OPT4001_DEFAULT_CONVERSION_TIME;
3269a960841SStefan Windfeldt-Prytz 
3279a960841SStefan Windfeldt-Prytz 	return opt4001_set_conf(chip);
3289a960841SStefan Windfeldt-Prytz }
3299a960841SStefan Windfeldt-Prytz 
opt4001_readable_reg(struct device * dev,unsigned int reg)3309a960841SStefan Windfeldt-Prytz static bool opt4001_readable_reg(struct device *dev, unsigned int reg)
3319a960841SStefan Windfeldt-Prytz {
3329a960841SStefan Windfeldt-Prytz 	switch (reg) {
3339a960841SStefan Windfeldt-Prytz 	case OPT4001_LIGHT1_MSB:
3349a960841SStefan Windfeldt-Prytz 	case OPT4001_LIGHT1_LSB:
3359a960841SStefan Windfeldt-Prytz 	case OPT4001_CTRL:
3369a960841SStefan Windfeldt-Prytz 	case OPT4001_DEVICE_ID:
3379a960841SStefan Windfeldt-Prytz 		return true;
3389a960841SStefan Windfeldt-Prytz 	default:
3399a960841SStefan Windfeldt-Prytz 		return false;
3409a960841SStefan Windfeldt-Prytz 	}
3419a960841SStefan Windfeldt-Prytz }
3429a960841SStefan Windfeldt-Prytz 
opt4001_writable_reg(struct device * dev,unsigned int reg)3439a960841SStefan Windfeldt-Prytz static bool opt4001_writable_reg(struct device *dev, unsigned int reg)
3449a960841SStefan Windfeldt-Prytz {
3459a960841SStefan Windfeldt-Prytz 	switch (reg) {
3469a960841SStefan Windfeldt-Prytz 	case OPT4001_CTRL:
3479a960841SStefan Windfeldt-Prytz 		return true;
3489a960841SStefan Windfeldt-Prytz 	default:
3499a960841SStefan Windfeldt-Prytz 		return false;
3509a960841SStefan Windfeldt-Prytz 	}
3519a960841SStefan Windfeldt-Prytz }
3529a960841SStefan Windfeldt-Prytz 
opt4001_volatile_reg(struct device * dev,unsigned int reg)3539a960841SStefan Windfeldt-Prytz static bool opt4001_volatile_reg(struct device *dev, unsigned int reg)
3549a960841SStefan Windfeldt-Prytz {
3559a960841SStefan Windfeldt-Prytz 	switch (reg) {
3569a960841SStefan Windfeldt-Prytz 	case OPT4001_LIGHT1_MSB:
3579a960841SStefan Windfeldt-Prytz 	case OPT4001_LIGHT1_LSB:
3589a960841SStefan Windfeldt-Prytz 		return true;
3599a960841SStefan Windfeldt-Prytz 	default:
3609a960841SStefan Windfeldt-Prytz 		return false;
3619a960841SStefan Windfeldt-Prytz 	}
3629a960841SStefan Windfeldt-Prytz }
3639a960841SStefan Windfeldt-Prytz 
3649a960841SStefan Windfeldt-Prytz static const struct regmap_config opt4001_regmap_config = {
3659a960841SStefan Windfeldt-Prytz 	.name = "opt4001",
3669a960841SStefan Windfeldt-Prytz 	.reg_bits = 8,
3679a960841SStefan Windfeldt-Prytz 	.val_bits = 16,
3689a960841SStefan Windfeldt-Prytz 	.cache_type = REGCACHE_RBTREE,
3699a960841SStefan Windfeldt-Prytz 	.max_register = OPT4001_DEVICE_ID,
3709a960841SStefan Windfeldt-Prytz 	.readable_reg = opt4001_readable_reg,
3719a960841SStefan Windfeldt-Prytz 	.writeable_reg = opt4001_writable_reg,
3729a960841SStefan Windfeldt-Prytz 	.volatile_reg = opt4001_volatile_reg,
3739a960841SStefan Windfeldt-Prytz 	.val_format_endian = REGMAP_ENDIAN_BIG,
3749a960841SStefan Windfeldt-Prytz };
3759a960841SStefan Windfeldt-Prytz 
opt4001_probe(struct i2c_client * client)3769a960841SStefan Windfeldt-Prytz static int opt4001_probe(struct i2c_client *client)
3779a960841SStefan Windfeldt-Prytz {
3789a960841SStefan Windfeldt-Prytz 	struct opt4001_chip *chip;
3799a960841SStefan Windfeldt-Prytz 	struct iio_dev *indio_dev;
3809a960841SStefan Windfeldt-Prytz 	int ret;
3819a960841SStefan Windfeldt-Prytz 	uint dev_id;
3829a960841SStefan Windfeldt-Prytz 
3839a960841SStefan Windfeldt-Prytz 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
3849a960841SStefan Windfeldt-Prytz 	if (!indio_dev)
3859a960841SStefan Windfeldt-Prytz 		return -ENOMEM;
3869a960841SStefan Windfeldt-Prytz 
3879a960841SStefan Windfeldt-Prytz 	chip = iio_priv(indio_dev);
3889a960841SStefan Windfeldt-Prytz 
3899a960841SStefan Windfeldt-Prytz 	ret = devm_regulator_get_enable(&client->dev, "vdd");
3909a960841SStefan Windfeldt-Prytz 	if (ret)
3919a960841SStefan Windfeldt-Prytz 		return dev_err_probe(&client->dev, ret, "Failed to enable vdd supply\n");
3929a960841SStefan Windfeldt-Prytz 
3939a960841SStefan Windfeldt-Prytz 	chip->regmap = devm_regmap_init_i2c(client, &opt4001_regmap_config);
3949a960841SStefan Windfeldt-Prytz 	if (IS_ERR(chip->regmap))
3959a960841SStefan Windfeldt-Prytz 		return dev_err_probe(&client->dev, PTR_ERR(chip->regmap),
3969a960841SStefan Windfeldt-Prytz 				     "regmap initialization failed\n");
3979a960841SStefan Windfeldt-Prytz 	chip->client = client;
3989a960841SStefan Windfeldt-Prytz 
3999a960841SStefan Windfeldt-Prytz 	indio_dev->info = &opt4001_info_no_irq;
4009a960841SStefan Windfeldt-Prytz 
4019a960841SStefan Windfeldt-Prytz 	ret = regmap_reinit_cache(chip->regmap, &opt4001_regmap_config);
4029a960841SStefan Windfeldt-Prytz 	if (ret)
4039a960841SStefan Windfeldt-Prytz 		return dev_err_probe(&client->dev, ret,
4049a960841SStefan Windfeldt-Prytz 				     "failed to reinit regmap cache\n");
4059a960841SStefan Windfeldt-Prytz 
4069a960841SStefan Windfeldt-Prytz 	ret = regmap_read(chip->regmap, OPT4001_DEVICE_ID, &dev_id);
4079a960841SStefan Windfeldt-Prytz 	if (ret < 0)
4089a960841SStefan Windfeldt-Prytz 		return dev_err_probe(&client->dev, ret,
4099a960841SStefan Windfeldt-Prytz 			"Failed to read the device ID register\n");
4109a960841SStefan Windfeldt-Prytz 
4119a960841SStefan Windfeldt-Prytz 	dev_id = FIELD_GET(OPT4001_DEVICE_ID_MASK, dev_id);
4129a960841SStefan Windfeldt-Prytz 	if (dev_id != OPT4001_DEVICE_ID_VAL)
4139a960841SStefan Windfeldt-Prytz 		dev_warn(&client->dev, "Device ID: %#04x unknown\n", dev_id);
4149a960841SStefan Windfeldt-Prytz 
4159a960841SStefan Windfeldt-Prytz 	chip->chip_info = device_get_match_data(&client->dev);
4169a960841SStefan Windfeldt-Prytz 
4179a960841SStefan Windfeldt-Prytz 	indio_dev->channels = opt4001_channels;
4189a960841SStefan Windfeldt-Prytz 	indio_dev->num_channels = ARRAY_SIZE(opt4001_channels);
4199a960841SStefan Windfeldt-Prytz 	indio_dev->modes = INDIO_DIRECT_MODE;
4209a960841SStefan Windfeldt-Prytz 	indio_dev->name = chip->chip_info->name;
4219a960841SStefan Windfeldt-Prytz 
4229a960841SStefan Windfeldt-Prytz 	ret = opt4001_load_defaults(chip);
4239a960841SStefan Windfeldt-Prytz 	if (ret < 0)
4249a960841SStefan Windfeldt-Prytz 		return dev_err_probe(&client->dev, ret,
4259a960841SStefan Windfeldt-Prytz 				     "Failed to set sensor defaults\n");
4269a960841SStefan Windfeldt-Prytz 
4279a960841SStefan Windfeldt-Prytz 	ret = devm_add_action_or_reset(&client->dev,
4289a960841SStefan Windfeldt-Prytz 					opt4001_chip_off_action,
4299a960841SStefan Windfeldt-Prytz 					chip);
4309a960841SStefan Windfeldt-Prytz 	if (ret < 0)
4319a960841SStefan Windfeldt-Prytz 		return dev_err_probe(&client->dev, ret,
4329a960841SStefan Windfeldt-Prytz 				     "Failed to setup power off action\n");
4339a960841SStefan Windfeldt-Prytz 
4349a960841SStefan Windfeldt-Prytz 	return devm_iio_device_register(&client->dev, indio_dev);
4359a960841SStefan Windfeldt-Prytz }
4369a960841SStefan Windfeldt-Prytz 
4379a960841SStefan Windfeldt-Prytz /*
4389a960841SStefan Windfeldt-Prytz  * The compatible string determines which constants to use depending on
4399a960841SStefan Windfeldt-Prytz  * opt4001 packaging
4409a960841SStefan Windfeldt-Prytz  */
4419a960841SStefan Windfeldt-Prytz static const struct i2c_device_id opt4001_id[] = {
4429a960841SStefan Windfeldt-Prytz 	{ "opt4001-sot-5x3", (kernel_ulong_t)&opt4001_sot_5x3_info },
4439a960841SStefan Windfeldt-Prytz 	{ "opt4001-picostar", (kernel_ulong_t)&opt4001_picostar_info },
4449a960841SStefan Windfeldt-Prytz 	{ }
4459a960841SStefan Windfeldt-Prytz };
4469a960841SStefan Windfeldt-Prytz MODULE_DEVICE_TABLE(i2c, opt4001_id);
4479a960841SStefan Windfeldt-Prytz 
4489a960841SStefan Windfeldt-Prytz static const struct of_device_id opt4001_of_match[] = {
4499a960841SStefan Windfeldt-Prytz 	{ .compatible = "ti,opt4001-sot-5x3", .data = &opt4001_sot_5x3_info},
4509a960841SStefan Windfeldt-Prytz 	{ .compatible = "ti,opt4001-picostar", .data = &opt4001_picostar_info},
4519a960841SStefan Windfeldt-Prytz 	{}
4529a960841SStefan Windfeldt-Prytz };
4539a960841SStefan Windfeldt-Prytz MODULE_DEVICE_TABLE(of, opt4001_of_match);
4549a960841SStefan Windfeldt-Prytz 
4559a960841SStefan Windfeldt-Prytz static struct i2c_driver opt4001_driver = {
4569a960841SStefan Windfeldt-Prytz 	.driver = {
4579a960841SStefan Windfeldt-Prytz 		.name = "opt4001",
4589a960841SStefan Windfeldt-Prytz 		.of_match_table = opt4001_of_match,
4599a960841SStefan Windfeldt-Prytz 	},
460*7cf15f42SUwe Kleine-König 	.probe = opt4001_probe,
4619a960841SStefan Windfeldt-Prytz 	.id_table = opt4001_id,
4629a960841SStefan Windfeldt-Prytz };
4639a960841SStefan Windfeldt-Prytz module_i2c_driver(opt4001_driver);
4649a960841SStefan Windfeldt-Prytz 
4659a960841SStefan Windfeldt-Prytz MODULE_AUTHOR("Stefan Windfeldt-Prytz <stefan.windfeldt-prytz@axis.com>");
4669a960841SStefan Windfeldt-Prytz MODULE_DESCRIPTION("Texas Instruments opt4001 ambient light sensor driver");
4679a960841SStefan Windfeldt-Prytz MODULE_LICENSE("GPL");
468