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, ®);
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