// SPDX-License-Identifier: GPL-2.0+ /* * VEML6046X00 High Accuracy RGBIR Color Sensor * * Copyright (c) 2025 Andreas Klinger */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Device registers * Those which are accessed as bulk io are omitted */ #define VEML6046X00_REG_CONF0 0x00 #define VEML6046X00_REG_CONF1 0x01 #define VEML6046X00_REG_THDH 0x04 #define VEML6046X00_REG_THDL 0x06 #define VEML6046X00_REG_R 0x10 #define VEML6046X00_REG_G 0x12 #define VEML6046X00_REG_B 0x14 #define VEML6046X00_REG_IR 0x16 #define VEML6046X00_REG_ID 0x18 #define VEML6046X00_REG_INT 0x1A #define VEML6046X00_REG_INT_H 0x1B /* Bit masks for specific functionality */ #define VEML6046X00_CONF0_ON_0 BIT(0) #define VEML6046X00_CONF0_INT BIT(1) #define VEML6046X00_CONF0_AF_TRIG BIT(2) #define VEML6046X00_CONF0_AF BIT(3) #define VEML6046X00_CONF0_IT GENMASK(6, 4) #define VEML6046X00_CONF1_CAL BIT(0) #define VEML6046X00_CONF1_PERS GENMASK(2, 1) #define VEML6046X00_CONF1_GAIN GENMASK(4, 3) #define VEML6046X00_CONF1_PD_D2 BIT(6) #define VEML6046X00_CONF1_ON_1 BIT(7) #define VEML6046X00_INT_TH_H BIT(1) #define VEML6046X00_INT_TH_L BIT(2) #define VEML6046X00_INT_DRDY BIT(3) #define VEML6046X00_INT_MASK \ (VEML6046X00_INT_TH_H | VEML6046X00_INT_TH_L | VEML6046X00_INT_DRDY) #define VEML6046X00_GAIN_1 0x0 #define VEML6046X00_GAIN_2 0x1 #define VEML6046X00_GAIN_0_66 0x2 #define VEML6046X00_GAIN_0_5 0x3 #define VEML6046X00_PD_2_2 0x0 #define VEML6046X00_PD_1_2 BIT(6) /* Autosuspend delay */ #define VEML6046X00_AUTOSUSPEND_MS (3 * MSEC_PER_SEC) enum veml6046x00_scan { VEML6046X00_SCAN_R, VEML6046X00_SCAN_G, VEML6046X00_SCAN_B, VEML6046X00_SCAN_IR, VEML6046X00_SCAN_TIMESTAMP, }; /** * struct veml6046x00_rf - Regmap field of configuration registers. * @int_en: Interrupt enable of green channel. * @mode: Mode of operation. * Driver uses always Active force mode. * @trig: Trigger to be set in active force mode for starting * measurement. * @it: Integration time. * @pers: Persistense - Number of threshold crossing for triggering * interrupt. */ struct veml6046x00_rf { struct regmap_field *int_en; struct regmap_field *mode; struct regmap_field *trig; struct regmap_field *it; struct regmap_field *pers; }; /** * struct veml6046x00_data - Private data of driver. * @regmap: Regmap definition of sensor. * @trig: Industrial-IO trigger. * @rf: Regmap field of configuration. */ struct veml6046x00_data { struct regmap *regmap; struct iio_trigger *trig; struct veml6046x00_rf rf; }; /** * DOC: Valid integration times (IT) * * static const int veml6046x00_it contains the array with valid IT. * * Register value to be read or written in regmap_field it on veml6046x00 is * identical with array index. * This means there is no separate translation table between valid integration * times and register values needed. The index of the array is identical with * the register value. * * The array is in the form as expected by the callback of the sysfs attribute * integration_time_available (IIO_CHAN_INFO_INT_TIME). So there is no * additional conversion needed. */ static const int veml6046x00_it[][2] = { { 0, 3125 }, { 0, 6250 }, { 0, 12500 }, { 0, 25000 }, { 0, 50000 }, { 0, 100000 }, { 0, 200000 }, { 0, 400000 }, }; /** * DOC: Handling of gain and photodiode size (PD) * * Gains here in the driver are not exactly the same as in the datasheet of the * sensor. The gain in the driver is a combination of the gain of the sensor * with the photodiode size (PD). * The following combinations are possible: * gain(driver) = gain(sensor) * PD * 0.25 = x0.5 * 1/2 * 0.33 = x0.66 * 1/2 * 0.5 = x0.5 * 2/2 * 0.66 = x0.66 * 2/2 * 1 = x1 * 2/2 * 2 = x2 * 2/2 */ /** * struct veml6046x00_gain_pd - Translation of gain and photodiode size (PD). * @gain_sen: Gain used in the sensor as described in the datasheet of the * sensor * @pd: Photodiode size in the sensor * * This is the translation table from the gain used in the driver (and also used * by the userspace interface in sysfs) to the gain and PD used in the sensor * hardware. * * There are six gain values visible to the user (0.25 .. 2) which translate to * two different gains in the sensor hardware (x0.5 .. x2) and two PD (1/2 and * 2/2). Theoretical are there eight combinations, but gain values 0.5 and 1 are * doubled and therefore the combination with the larger PD (2/2) is taken as * more photodiode cells are supposed to deliver a more precise result. */ struct veml6046x00_gain_pd { unsigned int gain_sen; unsigned int pd; }; static const struct veml6046x00_gain_pd veml6046x00_gain_pd[] = { { .gain_sen = VEML6046X00_GAIN_0_5, .pd = VEML6046X00_PD_1_2 }, { .gain_sen = VEML6046X00_GAIN_0_66, .pd = VEML6046X00_PD_1_2 }, { .gain_sen = VEML6046X00_GAIN_0_5, .pd = VEML6046X00_PD_2_2 }, { .gain_sen = VEML6046X00_GAIN_0_66, .pd = VEML6046X00_PD_2_2 }, { .gain_sen = VEML6046X00_GAIN_1, .pd = VEML6046X00_PD_2_2 }, { .gain_sen = VEML6046X00_GAIN_2, .pd = VEML6046X00_PD_2_2 }, }; /** * DOC: Factors for calculation of lux * * static const int veml6046x00_it_gains contains the factors for calculation of * lux. * * Depending on the set up integration time (IT), gain and photodiode size (PD) * the measured raw values are different if the light is constant. As the gain * and PD are already coupled in the driver (see &struct veml6046x00_gain_pd) * there are two dimensions remaining: IT and gain(driver). * * The array of available factors for a certain IT are grouped together in the * same form as expected by the callback of scale_available * (IIO_CHAN_INFO_SCALE). * * Factors for lux / raw count are taken directly from the datasheet. */ static const int veml6046x00_it_gains[][6][2] = { /* integration time: 3.125 ms */ { { 5, 376000 }, /* gain: x0.25 */ { 4, 72700 }, /* gain: x0.33 */ { 2, 688000 }, /* gain: x0.5 */ { 2, 36400 }, /* gain: x0.66 */ { 1, 344000 }, /* gain: x1 */ { 0, 672000 }, /* gain: x2 */ }, /* integration time: 6.25 ms */ { { 2, 688000 }, /* gain: x0.25 */ { 2, 36350 }, /* gain: x0.33 */ { 1, 344000 }, /* gain: x0.5 */ { 1, 18200 }, /* gain: x0.66 */ { 0, 672000 }, /* gain: x1 */ { 0, 336000 }, /* gain: x2 */ }, /* integration time: 12.5 ms */ { { 1, 344000 }, /* gain: x0.25 */ { 1, 18175 }, /* gain: x0.33 */ { 0, 672000 }, /* gain: x0.5 */ { 0, 509100 }, /* gain: x0.66 */ { 0, 336000 }, /* gain: x1 */ { 0, 168000 }, /* gain: x2 */ }, /* integration time: 25 ms */ { { 0, 672000 }, /* gain: x0.25 */ { 0, 509087 }, /* gain: x0.33 */ { 0, 336000 }, /* gain: x0.5 */ { 0, 254550 }, /* gain: x0.66 */ { 0, 168000 }, /* gain: x1 */ { 0, 84000 }, /* gain: x2 */ }, /* integration time: 50 ms */ { { 0, 336000 }, /* gain: x0.25 */ { 0, 254543 }, /* gain: x0.33 */ { 0, 168000 }, /* gain: x0.5 */ { 0, 127275 }, /* gain: x0.66 */ { 0, 84000 }, /* gain: x1 */ { 0, 42000 }, /* gain: x2 */ }, /* integration time: 100 ms */ { { 0, 168000 }, /* gain: x0.25 */ { 0, 127271 }, /* gain: x0.33 */ { 0, 84000 }, /* gain: x0.5 */ { 0, 63637 }, /* gain: x0.66 */ { 0, 42000 }, /* gain: x1 */ { 0, 21000 }, /* gain: x2 */ }, /* integration time: 200 ms */ { { 0, 84000 }, /* gain: x0.25 */ { 0, 63635 }, /* gain: x0.33 */ { 0, 42000 }, /* gain: x0.5 */ { 0, 31818 }, /* gain: x0.66 */ { 0, 21000 }, /* gain: x1 */ { 0, 10500 }, /* gain: x2 */ }, /* integration time: 400 ms */ { { 0, 42000 }, /* gain: x0.25 */ { 0, 31817 }, /* gain: x0.33 */ { 0, 21000 }, /* gain: x0.5 */ { 0, 15909 }, /* gain: x0.66 */ { 0, 10500 }, /* gain: x1 */ { 0, 5250 }, /* gain: x2 */ }, }; /* * Two bits (RGB_ON_0 and RGB_ON_1) must be cleared to power on the device. */ static int veml6046x00_power_on(struct veml6046x00_data *data) { int ret; struct device *dev = regmap_get_device(data->regmap); ret = regmap_clear_bits(data->regmap, VEML6046X00_REG_CONF0, VEML6046X00_CONF0_ON_0); if (ret) { dev_err(dev, "Failed to set bit for power on %d\n", ret); return ret; } return regmap_clear_bits(data->regmap, VEML6046X00_REG_CONF1, VEML6046X00_CONF1_ON_1); } /* * Two bits (RGB_ON_0 and RGB_ON_1) must be set to power off the device. */ static int veml6046x00_shutdown(struct veml6046x00_data *data) { int ret; struct device *dev = regmap_get_device(data->regmap); ret = regmap_set_bits(data->regmap, VEML6046X00_REG_CONF0, VEML6046X00_CONF0_ON_0); if (ret) { dev_err(dev, "Failed to set bit for shutdown %d\n", ret); return ret; } return regmap_set_bits(data->regmap, VEML6046X00_REG_CONF1, VEML6046X00_CONF1_ON_1); } static void veml6046x00_shutdown_action(void *data) { veml6046x00_shutdown(data); } static const struct iio_chan_spec veml6046x00_channels[] = { { .type = IIO_INTENSITY, .address = VEML6046X00_REG_R, .modified = 1, .channel2 = IIO_MOD_LIGHT_RED, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE), .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE), .scan_index = VEML6046X00_SCAN_R, .scan_type = { .sign = 'u', .realbits = 16, .storagebits = 16, .endianness = IIO_LE, }, }, { .type = IIO_INTENSITY, .address = VEML6046X00_REG_G, .modified = 1, .channel2 = IIO_MOD_LIGHT_GREEN, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE), .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE), .scan_index = VEML6046X00_SCAN_G, .scan_type = { .sign = 'u', .realbits = 16, .storagebits = 16, .endianness = IIO_LE, }, }, { .type = IIO_INTENSITY, .address = VEML6046X00_REG_B, .modified = 1, .channel2 = IIO_MOD_LIGHT_BLUE, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE), .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE), .scan_index = VEML6046X00_SCAN_B, .scan_type = { .sign = 'u', .realbits = 16, .storagebits = 16, .endianness = IIO_LE, }, }, { .type = IIO_INTENSITY, .address = VEML6046X00_REG_IR, .modified = 1, .channel2 = IIO_MOD_LIGHT_IR, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE), .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE), .scan_index = VEML6046X00_SCAN_IR, .scan_type = { .sign = 'u', .realbits = 16, .storagebits = 16, .endianness = IIO_LE, }, }, IIO_CHAN_SOFT_TIMESTAMP(VEML6046X00_SCAN_TIMESTAMP), }; static const struct regmap_config veml6046x00_regmap_config = { .name = "veml6046x00_regm", .reg_bits = 8, .val_bits = 8, .max_register = VEML6046X00_REG_INT_H, }; static const struct reg_field veml6046x00_rf_int_en = REG_FIELD(VEML6046X00_REG_CONF0, 1, 1); static const struct reg_field veml6046x00_rf_trig = REG_FIELD(VEML6046X00_REG_CONF0, 2, 2); static const struct reg_field veml6046x00_rf_mode = REG_FIELD(VEML6046X00_REG_CONF0, 3, 3); static const struct reg_field veml6046x00_rf_it = REG_FIELD(VEML6046X00_REG_CONF0, 4, 6); static const struct reg_field veml6046x00_rf_pers = REG_FIELD(VEML6046X00_REG_CONF1, 1, 2); static int veml6046x00_regfield_init(struct veml6046x00_data *data) { struct regmap *regmap = data->regmap; struct device *dev = regmap_get_device(data->regmap); struct regmap_field *rm_field; struct veml6046x00_rf *rf = &data->rf; rm_field = devm_regmap_field_alloc(dev, regmap, veml6046x00_rf_int_en); if (IS_ERR(rm_field)) return PTR_ERR(rm_field); rf->int_en = rm_field; rm_field = devm_regmap_field_alloc(dev, regmap, veml6046x00_rf_mode); if (IS_ERR(rm_field)) return PTR_ERR(rm_field); rf->mode = rm_field; rm_field = devm_regmap_field_alloc(dev, regmap, veml6046x00_rf_trig); if (IS_ERR(rm_field)) return PTR_ERR(rm_field); rf->trig = rm_field; rm_field = devm_regmap_field_alloc(dev, regmap, veml6046x00_rf_it); if (IS_ERR(rm_field)) return PTR_ERR(rm_field); rf->it = rm_field; rm_field = devm_regmap_field_alloc(dev, regmap, veml6046x00_rf_pers); if (IS_ERR(rm_field)) return PTR_ERR(rm_field); rf->pers = rm_field; return 0; } static int veml6046x00_get_it_index(struct veml6046x00_data *data) { int ret; unsigned int reg; ret = regmap_field_read(data->rf.it, ®); if (ret) return ret; /* register value is identical with index of array */ if (reg >= ARRAY_SIZE(veml6046x00_it)) return -EINVAL; return reg; } static int veml6046x00_get_it_usec(struct veml6046x00_data *data, unsigned int *it_usec) { int ret; unsigned int reg; ret = regmap_field_read(data->rf.it, ®); if (ret) return ret; if (reg >= ARRAY_SIZE(veml6046x00_it)) return -EINVAL; *it_usec = veml6046x00_it[reg][1]; return IIO_VAL_INT_PLUS_MICRO; } static int veml6046x00_set_it(struct iio_dev *iio, int val, int val2) { struct veml6046x00_data *data = iio_priv(iio); unsigned int i; for (i = 0; i < ARRAY_SIZE(veml6046x00_it); i++) { if ((veml6046x00_it[i][0] == val) && (veml6046x00_it[i][1] == val2)) return regmap_field_write(data->rf.it, i); } return -EINVAL; } static int veml6046x00_get_val_gain_idx(struct veml6046x00_data *data, int val, int val2) { unsigned int i; int it_idx; it_idx = veml6046x00_get_it_index(data); if (it_idx < 0) return it_idx; for (i = 0; i < ARRAY_SIZE(veml6046x00_it_gains[it_idx]); i++) { if ((veml6046x00_it_gains[it_idx][i][0] == val) && (veml6046x00_it_gains[it_idx][i][1] == val2)) return i; } return -EINVAL; } static int veml6046x00_get_gain_idx(struct veml6046x00_data *data) { int ret; unsigned int i, reg, reg_gain, reg_pd; ret = regmap_read(data->regmap, VEML6046X00_REG_CONF1, ®); if (ret) return ret; reg_gain = FIELD_GET(VEML6046X00_CONF1_GAIN, reg); reg_pd = reg & VEML6046X00_CONF1_PD_D2; for (i = 0; i < ARRAY_SIZE(veml6046x00_gain_pd); i++) { if ((veml6046x00_gain_pd[i].gain_sen == reg_gain) && (veml6046x00_gain_pd[i].pd == reg_pd)) return i; } return -EINVAL; } static int veml6046x00_set_scale(struct iio_dev *iio, int val, int val2) { struct veml6046x00_data *data = iio_priv(iio); unsigned int new_scale; int gain_idx; gain_idx = veml6046x00_get_val_gain_idx(data, val, val2); if (gain_idx < 0) return gain_idx; new_scale = FIELD_PREP(VEML6046X00_CONF1_GAIN, veml6046x00_gain_pd[gain_idx].gain_sen) | veml6046x00_gain_pd[gain_idx].pd; return regmap_update_bits(data->regmap, VEML6046X00_REG_CONF1, VEML6046X00_CONF1_GAIN | VEML6046X00_CONF1_PD_D2, new_scale); } static int veml6046x00_get_scale(struct veml6046x00_data *data, int *val, int *val2) { int gain_idx, it_idx; gain_idx = veml6046x00_get_gain_idx(data); if (gain_idx < 0) return gain_idx; it_idx = veml6046x00_get_it_index(data); if (it_idx < 0) return it_idx; *val = veml6046x00_it_gains[it_idx][gain_idx][0]; *val2 = veml6046x00_it_gains[it_idx][gain_idx][1]; return IIO_VAL_INT_PLUS_MICRO; } /** * veml6046x00_read_data_ready() - Read data ready bit * @data: Private data. * * Helper function for reading data ready bit from interrupt register. * * Return: * * %1 - Data is available (AF_DATA_READY is set) * * %0 - No data available * * %-EIO - Error during bulk read */ static int veml6046x00_read_data_ready(struct veml6046x00_data *data) { struct device *dev = regmap_get_device(data->regmap); int ret; u8 reg[2]; /* * Note from the vendor, but not explicitly in the datasheet: we * should always read both registers together. */ ret = regmap_bulk_read(data->regmap, VEML6046X00_REG_INT, ®, sizeof(reg)); if (ret) { dev_err(dev, "Failed to read interrupt register %d\n", ret); return -EIO; } if (reg[1] & VEML6046X00_INT_DRDY) return 1; return 0; } /** * veml6046x00_wait_data_available() - Wait until data is available * @iio: Industrial IO. * @usecs: Microseconds to wait for data. * * This function waits for a certain bit in the interrupt register which signals * that there is data to be read available. * * It tries it two times with a waiting time of usecs in between. * * Return: * * %1 - Data is available (AF_DATA_READY is set) * * %0 - Timeout, no data available after usecs timeout * * %-EIO - Error during bulk read */ static int veml6046x00_wait_data_available(struct iio_dev *iio, unsigned int usecs) { struct veml6046x00_data *data = iio_priv(iio); int ret; ret = veml6046x00_read_data_ready(data); if (ret) return ret; fsleep(usecs); return veml6046x00_read_data_ready(data); } static int veml6046x00_single_read(struct iio_dev *iio, enum iio_modifier modifier, int *val) { struct veml6046x00_data *data = iio_priv(iio); struct device *dev = regmap_get_device(data->regmap); unsigned int addr, it_usec; int ret; __le16 reg; switch (modifier) { case IIO_MOD_LIGHT_RED: addr = VEML6046X00_REG_R; break; case IIO_MOD_LIGHT_GREEN: addr = VEML6046X00_REG_G; break; case IIO_MOD_LIGHT_BLUE: addr = VEML6046X00_REG_B; break; case IIO_MOD_LIGHT_IR: addr = VEML6046X00_REG_IR; break; default: return -EINVAL; } ret = pm_runtime_resume_and_get(dev); if (ret) return ret; ret = veml6046x00_get_it_usec(data, &it_usec); if (ret < 0) { dev_err(dev, "Failed to get integration time ret: %d", ret); goto out; } ret = regmap_field_write(data->rf.mode, 1); if (ret) { dev_err(dev, "Failed to write mode ret: %d", ret); goto out; } ret = regmap_field_write(data->rf.trig, 1); if (ret) { dev_err(dev, "Failed to write trigger ret: %d", ret); goto out; } /* integration time + 12.5 % to ensure completion */ fsleep(it_usec + it_usec / 8); ret = veml6046x00_wait_data_available(iio, it_usec * 4); if (ret < 0) goto out; if (ret == 0) { ret = -EAGAIN; goto out; } if (!iio_device_claim_direct(iio)) { ret = -EBUSY; goto out; } ret = regmap_bulk_read(data->regmap, addr, ®, sizeof(reg)); iio_device_release_direct(iio); if (ret) goto out; *val = le16_to_cpu(reg); ret = IIO_VAL_INT; out: pm_runtime_put_autosuspend(dev); return ret; } static int veml6046x00_read_raw(struct iio_dev *iio, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { struct veml6046x00_data *data = iio_priv(iio); switch (mask) { case IIO_CHAN_INFO_RAW: if (chan->type != IIO_INTENSITY) return -EINVAL; return veml6046x00_single_read(iio, chan->channel2, val); case IIO_CHAN_INFO_INT_TIME: *val = 0; return veml6046x00_get_it_usec(data, val2); case IIO_CHAN_INFO_SCALE: return veml6046x00_get_scale(data, val, val2); default: return -EINVAL; } } static int veml6046x00_read_avail(struct iio_dev *iio, struct iio_chan_spec const *chan, const int **vals, int *type, int *length, long mask) { struct veml6046x00_data *data = iio_priv(iio); int it_idx; switch (mask) { case IIO_CHAN_INFO_INT_TIME: *vals = (int *)&veml6046x00_it; *length = 2 * ARRAY_SIZE(veml6046x00_it); *type = IIO_VAL_INT_PLUS_MICRO; return IIO_AVAIL_LIST; case IIO_CHAN_INFO_SCALE: it_idx = veml6046x00_get_it_index(data); if (it_idx < 0) return it_idx; *vals = (int *)&veml6046x00_it_gains[it_idx]; *length = 2 * ARRAY_SIZE(veml6046x00_it_gains[it_idx]); *type = IIO_VAL_INT_PLUS_MICRO; return IIO_AVAIL_LIST; default: return -EINVAL; } } static int veml6046x00_write_raw(struct iio_dev *iio, struct iio_chan_spec const *chan, int val, int val2, long mask) { switch (mask) { case IIO_CHAN_INFO_INT_TIME: return veml6046x00_set_it(iio, val, val2); case IIO_CHAN_INFO_SCALE: return veml6046x00_set_scale(iio, val, val2); default: return -EINVAL; } } static const struct iio_info veml6046x00_info_no_irq = { .read_raw = veml6046x00_read_raw, .read_avail = veml6046x00_read_avail, .write_raw = veml6046x00_write_raw, }; static int veml6046x00_buffer_preenable(struct iio_dev *iio) { struct veml6046x00_data *data = iio_priv(iio); struct device *dev = regmap_get_device(data->regmap); int ret; ret = regmap_field_write(data->rf.mode, 0); if (ret) { dev_err(dev, "Failed to set mode %d\n", ret); return ret; } ret = regmap_field_write(data->rf.trig, 0); if (ret) { /* * no unrolling of mode as it is set appropriately with next * single read. */ dev_err(dev, "Failed to set trigger %d\n", ret); return ret; } return pm_runtime_resume_and_get(dev); } static int veml6046x00_buffer_postdisable(struct iio_dev *iio) { struct veml6046x00_data *data = iio_priv(iio); struct device *dev = regmap_get_device(data->regmap); int ret; ret = regmap_field_write(data->rf.mode, 1); if (ret) { dev_err(dev, "Failed to set mode %d\n", ret); return ret; } pm_runtime_put_autosuspend(dev); return 0; } static const struct iio_buffer_setup_ops veml6046x00_buffer_setup_ops = { .preenable = veml6046x00_buffer_preenable, .postdisable = veml6046x00_buffer_postdisable, }; static irqreturn_t veml6046x00_trig_handler(int irq, void *p) { struct iio_poll_func *pf = p; struct iio_dev *iio = pf->indio_dev; struct veml6046x00_data *data = iio_priv(iio); int ret; struct { __le16 chans[4]; aligned_s64 timestamp; } scan; ret = regmap_bulk_read(data->regmap, VEML6046X00_REG_R, &scan.chans, sizeof(scan.chans)); if (ret) goto done; iio_push_to_buffers_with_ts(iio, &scan, sizeof(scan), iio_get_time_ns(iio)); done: iio_trigger_notify_done(iio->trig); return IRQ_HANDLED; } static int veml6046x00_validate_part_id(struct veml6046x00_data *data) { struct device *dev = regmap_get_device(data->regmap); unsigned int part_id; int ret; __le16 reg; ret = regmap_bulk_read(data->regmap, VEML6046X00_REG_ID, ®, sizeof(reg)); if (ret) return dev_err_probe(dev, ret, "Failed to read ID\n"); part_id = le16_to_cpu(reg); if (part_id != 0x01) dev_info(dev, "Unknown ID %#04x\n", part_id); return 0; } static int veml6046x00_setup_device(struct iio_dev *iio) { struct veml6046x00_data *data = iio_priv(iio); struct device *dev = regmap_get_device(data->regmap); int ret; __le16 reg16; reg16 = cpu_to_le16(VEML6046X00_CONF0_AF); ret = regmap_bulk_write(data->regmap, VEML6046X00_REG_CONF0, ®16, sizeof(reg16)); if (ret) return dev_err_probe(dev, ret, "Failed to set configuration\n"); reg16 = cpu_to_le16(0); ret = regmap_bulk_write(data->regmap, VEML6046X00_REG_THDL, ®16, sizeof(reg16)); if (ret) return dev_err_probe(dev, ret, "Failed to set low threshold\n"); reg16 = cpu_to_le16(U16_MAX); ret = regmap_bulk_write(data->regmap, VEML6046X00_REG_THDH, ®16, sizeof(reg16)); if (ret) return dev_err_probe(dev, ret, "Failed to set high threshold\n"); ret = regmap_bulk_read(data->regmap, VEML6046X00_REG_INT, ®16, sizeof(reg16)); if (ret) return dev_err_probe(dev, ret, "Failed to clear interrupts\n"); return 0; } static int veml6046x00_probe(struct i2c_client *i2c) { struct device *dev = &i2c->dev; struct veml6046x00_data *data; struct iio_dev *iio; struct regmap *regmap; int ret; regmap = devm_regmap_init_i2c(i2c, &veml6046x00_regmap_config); if (IS_ERR(regmap)) return dev_err_probe(dev, PTR_ERR(regmap), "Failed to set regmap\n"); iio = devm_iio_device_alloc(dev, sizeof(*data)); if (!iio) return -ENOMEM; data = iio_priv(iio); /* struct iio_dev is retrieved via dev_get_drvdata(). */ i2c_set_clientdata(i2c, iio); data->regmap = regmap; ret = veml6046x00_regfield_init(data); if (ret) return dev_err_probe(dev, ret, "Failed to init regfield\n"); ret = devm_regulator_get_enable(dev, "vdd"); if (ret) return dev_err_probe(dev, ret, "Failed to enable regulator\n"); /* bring device in a known state and switch device on */ ret = veml6046x00_setup_device(iio); if (ret < 0) return ret; ret = devm_add_action_or_reset(dev, veml6046x00_shutdown_action, data); if (ret < 0) return dev_err_probe(dev, ret, "Failed to add shut down action\n"); ret = pm_runtime_set_active(dev); if (ret < 0) return dev_err_probe(dev, ret, "Failed to activate PM runtime\n"); ret = devm_pm_runtime_enable(dev); if (ret) return dev_err_probe(dev, ret, "Failed to enable PM runtime\n"); pm_runtime_get_noresume(dev); pm_runtime_set_autosuspend_delay(dev, VEML6046X00_AUTOSUSPEND_MS); pm_runtime_use_autosuspend(dev); ret = veml6046x00_validate_part_id(data); if (ret) return dev_err_probe(dev, ret, "Failed to validate device ID\n"); iio->name = "veml6046x00"; iio->channels = veml6046x00_channels; iio->num_channels = ARRAY_SIZE(veml6046x00_channels); iio->modes = INDIO_DIRECT_MODE; iio->info = &veml6046x00_info_no_irq; ret = devm_iio_triggered_buffer_setup(dev, iio, NULL, veml6046x00_trig_handler, &veml6046x00_buffer_setup_ops); if (ret) return dev_err_probe(dev, ret, "Failed to register triggered buffer"); pm_runtime_put_autosuspend(dev); ret = devm_iio_device_register(dev, iio); if (ret) return dev_err_probe(dev, ret, "Failed to register iio device"); return 0; } static int veml6046x00_runtime_suspend(struct device *dev) { struct veml6046x00_data *data = iio_priv(dev_get_drvdata(dev)); return veml6046x00_shutdown(data); } static int veml6046x00_runtime_resume(struct device *dev) { struct veml6046x00_data *data = iio_priv(dev_get_drvdata(dev)); return veml6046x00_power_on(data); } static DEFINE_RUNTIME_DEV_PM_OPS(veml6046x00_pm_ops, veml6046x00_runtime_suspend, veml6046x00_runtime_resume, NULL); static const struct of_device_id veml6046x00_of_match[] = { { .compatible = "vishay,veml6046x00" }, { } }; MODULE_DEVICE_TABLE(of, veml6046x00_of_match); static const struct i2c_device_id veml6046x00_id[] = { { "veml6046x00" }, { } }; MODULE_DEVICE_TABLE(i2c, veml6046x00_id); static struct i2c_driver veml6046x00_driver = { .driver = { .name = "veml6046x00", .of_match_table = veml6046x00_of_match, .pm = pm_ptr(&veml6046x00_pm_ops), }, .probe = veml6046x00_probe, .id_table = veml6046x00_id, }; module_i2c_driver(veml6046x00_driver); MODULE_AUTHOR("Andreas Klinger "); MODULE_DESCRIPTION("VEML6046X00 RGBIR Color Sensor"); MODULE_LICENSE("GPL");