#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/dev_printk.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/time.h>
#include <linux/types.h>
#include <linux/units.h>
#include <asm/byteorder.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#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
#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)
#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 {
struct regmap_field *int_en;
struct regmap_field *mode;
struct regmap_field *trig;
struct regmap_field *it;
struct regmap_field *pers;
};
struct veml6046x00_data {
struct regmap *regmap;
struct iio_trigger *trig;
struct veml6046x00_rf rf;
};
static const int veml6046x00_it[][2] = {
{ 0, 3125 },
{ 0, 6250 },
{ 0, 12500 },
{ 0, 25000 },
{ 0, 50000 },
{ 0, 100000 },
{ 0, 200000 },
{ 0, 400000 },
};
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 },
};
static const int veml6046x00_it_gains[][6][2] = {
{
{ 5, 376000 },
{ 4, 72700 },
{ 2, 688000 },
{ 2, 36400 },
{ 1, 344000 },
{ 0, 672000 },
},
{
{ 2, 688000 },
{ 2, 36350 },
{ 1, 344000 },
{ 1, 18200 },
{ 0, 672000 },
{ 0, 336000 },
},
{
{ 1, 344000 },
{ 1, 18175 },
{ 0, 672000 },
{ 0, 509100 },
{ 0, 336000 },
{ 0, 168000 },
},
{
{ 0, 672000 },
{ 0, 509087 },
{ 0, 336000 },
{ 0, 254550 },
{ 0, 168000 },
{ 0, 84000 },
},
{
{ 0, 336000 },
{ 0, 254543 },
{ 0, 168000 },
{ 0, 127275 },
{ 0, 84000 },
{ 0, 42000 },
},
{
{ 0, 168000 },
{ 0, 127271 },
{ 0, 84000 },
{ 0, 63637 },
{ 0, 42000 },
{ 0, 21000 },
},
{
{ 0, 84000 },
{ 0, 63635 },
{ 0, 42000 },
{ 0, 31818 },
{ 0, 21000 },
{ 0, 10500 },
},
{
{ 0, 42000 },
{ 0, 31817 },
{ 0, 21000 },
{ 0, 15909 },
{ 0, 10500 },
{ 0, 5250 },
},
};
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);
}
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;
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;
}
static int veml6046x00_read_data_ready(struct veml6046x00_data *data)
{
struct device *dev = regmap_get_device(data->regmap);
int ret;
u8 reg[2];
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;
}
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;
}
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) {
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);
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");
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 <ak@it-klinger.de>");
MODULE_DESCRIPTION("VEML6046X00 RGBIR Color Sensor");
MODULE_LICENSE("GPL");