Re: [PATCH 1/2] iio: light: add driver for veml6030 ambient light sensor

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Thanks Peter, 2 questions inline.
On Wed, Sep 18, 2019 at 1:40 PM Peter Meerwald-Stadler
<pmeerw@xxxxxxxxxx> wrote:
>
> On Wed, 18 Sep 2019, Rishi Gupta wrote:
>
> > veml6030 is an ambient light sensor from Vishay semiconductors.
> > It has 16-bit resolution, supports both ambient light measurement
> > and white channel which is more responsive to wider wavelength
> > spectrum. It has flexible power saving, integration time and
> > gain options. Communication with host is over I2C.
>
> some comments below
>
> > Signed-off-by: Rishi Gupta <gupt21@xxxxxxxxx>
> > ---
> >  drivers/iio/light/Kconfig    |  11 +
> >  drivers/iio/light/Makefile   |   1 +
> >  drivers/iio/light/veml6030.c | 647 +++++++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 659 insertions(+)
> >  create mode 100644 drivers/iio/light/veml6030.c
> >
> > diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
> > index e3fd00b..9afe997 100644
> > --- a/drivers/iio/light/Kconfig
> > +++ b/drivers/iio/light/Kconfig
> > @@ -484,6 +484,17 @@ config VCNL4035
> >         To compile this driver as a module, choose M here: the
> >         module will be called vcnl4035.
> >
> > +config VEML6030
> > +     tristate "VEML6030 ambient light sensor"
> > +     select REGMAP_I2C
> > +     depends on I2C
> > +     help
> > +       Say Y here if you want to build a driver for the Vishay VEML6030
> > +       ambient light sensor (ALS).
> > +
> > +       To compile this driver as a module, choose M here: the
> > +       module will be called veml6030.
> > +
> >  config VEML6070
> >       tristate "VEML6070 UV A light sensor"
> >       depends on I2C
> > diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
> > index e40794f..011968b 100644
> > --- a/drivers/iio/light/Makefile
> > +++ b/drivers/iio/light/Makefile
> > @@ -47,6 +47,7 @@ obj-$(CONFIG_TSL4531)               += tsl4531.o
> >  obj-$(CONFIG_US5182D)                += us5182d.o
> >  obj-$(CONFIG_VCNL4000)               += vcnl4000.o
> >  obj-$(CONFIG_VCNL4035)               += vcnl4035.o
> > +obj-$(CONFIG_VEML6030)               += veml6030.o
> >  obj-$(CONFIG_VEML6070)               += veml6070.o
> >  obj-$(CONFIG_VL6180)         += vl6180.o
> >  obj-$(CONFIG_ZOPT2201)               += zopt2201.o
> > diff --git a/drivers/iio/light/veml6030.c b/drivers/iio/light/veml6030.c
> > new file mode 100644
> > index 0000000..b12cdce
> > --- /dev/null
> > +++ b/drivers/iio/light/veml6030.c
> > @@ -0,0 +1,647 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * VEML6030 Ambient Light Sensor
> > + *
> > + * Copyright (c) 2019, Rishi Gupta <gupt21@xxxxxxxxx>
> > + *
> > + * Datasheet: https://www.vishay.com/docs/84366/veml6030.pdf
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/i2c.h>
> > +#include <linux/err.h>
> > +#include <linux/regmap.h>
> > +#include <linux/mutex.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/iio/iio.h>
> > +#include <linux/iio/sysfs.h>
> > +#include <linux/iio/events.h>
> > +
> > +/* Device registers */
> > +#define VEML6030_REG_ALS_CONF    0x00
> > +#define VEML6030_REG_ALS_WH      0x01
> > +#define VEML6030_REG_ALS_WL      0x02
> > +#define VEML6030_REG_ALS_PSM     0x03
> > +#define VEML6030_REG_ALS_DATA    0x04
> > +#define VEML6030_REG_WHITE_DATA  0x05
> > +#define VEML6030_REG_ALS_INT     0x06
> > +
> > +/* Bit masks for specific functionality */
> > +#define VEML6030_ALS_IT       GENMASK(9, 6)
> > +#define VEML6030_PSM          GENMASK(2, 1)
> > +#define VEML6030_ALS_PERS     GENMASK(5, 4)
> > +#define VEML6030_ALS_GAIN     GENMASK(12, 11)
> > +#define VEML6030_PSM_EN       BIT(0)
> > +#define VEML6030_INT_TH_LOW   BIT(15)
> > +#define VEML6030_INT_TH_HIGH  BIT(14)
> > +#define VEML6030_ALS_INT_EN   BIT(1)
> > +#define VEML6030_ALS_SD       BIT(0)
> > +
> > +struct veml6030_data {
> > +     struct i2c_client *client;
> > +     struct regmap *regmap;
> > +};
> > +
> > +static IIO_CONST_ATTR_INT_TIME_AVAIL("0.025 0.05 0.1 0.2 0.4 0.8");
> > +
> > +static struct attribute *veml6030_attributes[] = {
> > +     &iio_const_attr_integration_time_available.dev_attr.attr,
> > +     NULL
> > +};
> > +
> > +static const struct attribute_group veml6030_attr_group = {
> > +     .attrs = veml6030_attributes,
> > +};
> > +
> > +/*
> > + * Scale is 1/gain. Value 0.125 is ALS gain x (1/8), 0.25 is
> > + * ALS gain x (1/4), 1.0 = ALS gain x 1 and 2.0 is ALS gain x 2.
> > + */
>
> can't IIO_CONST_ATTR() be used?
Do you mean, I should use IIO-CONST_ATTR for custom sysfs entries. If
yes, is this what you mean:
https://lore.kernel.org/linux-iio/CALUj-gsjKkiF8vrzgeWkh9wo-qBuJDW+5eLPqHqdj3v3ngk4UA@xxxxxxxxxxxxxx
>
> > +static ssize_t veml6030_scale_available(struct iio_dev *indio_dev,
> > +     uintptr_t private, const struct iio_chan_spec *chan, char *buf)
> > +{
> > +     return sprintf(buf, "%s\n", "0.125 0.25 1.0 2.0");
> > +}
> > +
> > +/*
> > + * Persistence = 1/2/4/8 x integration time
> > + * Minimum time for which light readings must stay above configured
> > + * threshold to assert interrupt.
> > + */
> > +static ssize_t veml6030_persistence_available(struct iio_dev *indio_dev,
> > +     uintptr_t private, const struct iio_chan_spec *chan, char *buf)
> > +{
> > +     return sprintf(buf, "%s\n", "1 2 4 8");
> > +}
> > +
> > +/*
> > + * Power saving modes supported.
> > + * 1/2/3/4 corresponds to PSM modes 1/2/3/4 respectively.
> > + */
> > +static ssize_t veml6030_psm_available(struct iio_dev *indio_dev,
> > +     uintptr_t private, const struct iio_chan_spec *chan, char *buf)
> > +{
> > +     return sprintf(buf, "%s\n", "1 2 3 4");
> > +}
> > +
> > +ssize_t veml6030_set_psm(struct iio_dev *indio_dev, uintptr_t priv,
> > +                     const struct iio_chan_spec *chan, const char *buf,
> > +                     size_t len)
> > +{
> > +     int ret;
> > +     unsigned int val;
> > +     struct veml6030_data *data = iio_priv(indio_dev);
> > +
> > +     ret = kstrtouint(buf, 0, &val);
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     if (val < 1 || val > 4)
> > +             return -EINVAL;
> > +
> > +     ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_PSM,
> > +                              VEML6030_PSM | VEML6030_PSM_EN, val - 1);
> > +     if (ret) {
> > +             dev_err(&data->client->dev,
> > +                             "can't update psm value %d\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     return len;
> > +}
> > +
> > +#define VEML6030_AVAIL(_name, _read) \
> > +{ \
> > +     .name = (_name "_available"), \
> > +     .read = _read, \
> > +     .shared = IIO_SEPARATE, \
> > +}
> > +
> > +static const struct iio_chan_spec_ext_info veml6030_ext_info[] = {
> > +     {
> > +             .name = "psm",
> > +             .write = veml6030_set_psm,
> > +             .shared = IIO_SEPARATE,
> > +     },
> > +     VEML6030_AVAIL("psm", veml6030_psm_available),
> > +     VEML6030_AVAIL("scale", veml6030_scale_available),
> > +     VEML6030_AVAIL("period", veml6030_persistence_available),
>
> psm and period is private API and should be documented separately
> (Documentation/ABI/testing/sysfs-bus-iio-...)
>
> > +     { },
> > +};
> > +
> > +static const struct iio_event_spec veml6030_event_spec[] = {
> > +     {
> > +             .type = IIO_EV_TYPE_THRESH,
> > +             .dir = IIO_EV_DIR_RISING,
> > +             .mask_separate = BIT(IIO_EV_INFO_VALUE),
> > +     }, {
> > +             .type = IIO_EV_TYPE_THRESH,
> > +             .dir = IIO_EV_DIR_FALLING,
> > +             .mask_separate = BIT(IIO_EV_INFO_VALUE),
> > +     }, {
> > +             .type = IIO_EV_TYPE_THRESH,
> > +             .dir = IIO_EV_DIR_EITHER,
> > +             .mask_separate = BIT(IIO_EV_INFO_PERIOD),
> > +     }, {
> > +             .type = IIO_EV_TYPE_THRESH,
> > +             .dir = IIO_EV_DIR_EITHER,
> > +             .mask_separate = BIT(IIO_EV_INFO_ENABLE),
> > +     },
> > +};
> > +
> > +/*
> > + * This is used to specify both scan index and numerical
> > + * channel number in channel specifications.
> > + */
> > +enum veml6030_chan {
> > +     CH_ALS,
> > +     CH_WHITE
>
> might be extended, end with comma
>
> > +};
> > +
> > +static const struct iio_chan_spec veml6030_channels[] = {
> > +     {
> > +             .type = IIO_LIGHT,
> > +             .channel = CH_ALS,
> > +             .scan_index = CH_ALS,
> > +             .scan_type = {
>
> driver doesn't seem to support buffered mode, hence .scan_type not needed
>
Do I need to remove .scan_index also or it is must to have field even
if buffered mode is not used.
> > +                     .sign = 'u',
> > +                     .realbits = 16,
> > +                     .storagebits = 16,
> > +                     .endianness = IIO_LE,
> > +             },
> > +             .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> > +                             BIT(IIO_CHAN_INFO_INT_TIME) |
> > +                             BIT(IIO_CHAN_INFO_SCALE),
> > +             .event_spec = veml6030_event_spec,
> > +             .num_event_specs = ARRAY_SIZE(veml6030_event_spec),
> > +             .ext_info = veml6030_ext_info,
> > +     },
> > +     {
> > +             .type = IIO_INTENSITY,
> > +             .channel = CH_WHITE,
> > +             .modified = 1,
> > +             .channel2 = IIO_MOD_LIGHT_BOTH,
> > +             .scan_index = CH_WHITE,
> > +             .scan_type = {
> > +                     .sign = 'u',
> > +                     .realbits = 16,
> > +                     .storagebits = 16,
> > +                     .endianness = IIO_LE,
> > +             },
> > +             .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
> > +     },
> > +};
> > +
> > +static const struct regmap_config veml6030_regmap_config = {
> > +     .name = "veml6030_regmap",
> > +     .reg_bits = 8,
> > +     .val_bits = 16,
> > +     .max_register = VEML6030_REG_ALS_INT,
> > +     .val_format_endian = REGMAP_ENDIAN_LITTLE,
> > +};
> > +
> > +static int veml6030_set_als_gain(struct iio_dev *indio_dev,
> > +                                             int val, int val2)
> > +{
> > +     int ret, gain;
> > +     struct veml6030_data *data = iio_priv(indio_dev);
> > +
> > +     if (val == 0 && val2 == 125000)
> > +             gain = 0x02;
> > +     else if (val == 0 && val2 == 250000)
> > +             gain = 0x03;
> > +     else if (val == 1 && val2 == 0)
> > +             gain = 0x00;
> > +     else if (val == 2 && val2 == 0)
> > +             gain = 0x01;
> > +     else
> > +             return -EINVAL;
> > +
> > +     ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF,
> > +                                     VEML6030_ALS_GAIN, gain);
> > +     if (ret) {
> > +             dev_err(&data->client->dev,
> > +                             "can't set ALS gain %d\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +static int veml6030_set_als_persistence(struct iio_dev *indio_dev,
> > +                                             int val, int val2)
> > +{
> > +     int ret;
> > +     struct veml6030_data *data = iio_priv(indio_dev);
> > +
> > +     if (val < 0 || val > 8 || hweight8(val) != 1 || val2)
> > +             return -EINVAL;
> > +
> > +     ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF,
> > +                                     VEML6030_ALS_PERS, val);
> > +     if (ret) {
> > +             dev_err(&data->client->dev,
> > +                             "can't set persistence value %d\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +static int veml6030_set_als_threshold(struct iio_dev *indio_dev,
> > +                                             int val, int val2, int dir)
> > +{
> > +     int ret;
> > +     struct veml6030_data *data = iio_priv(indio_dev);
> > +
> > +     if (val > 0xFFFF || val < 0 || val2)
> > +             return -EINVAL;
> > +
> > +     if (dir == IIO_EV_DIR_RISING) {
> > +             ret = regmap_write(data->regmap, VEML6030_REG_ALS_WH, val);
> > +             if (ret)
> > +                     dev_err(&data->client->dev,
> > +                                     "can't set high threshold %d\n", ret);
> > +     } else {
> > +             ret = regmap_write(data->regmap, VEML6030_REG_ALS_WL, val);
> > +             if (ret)
> > +                     dev_err(&data->client->dev,
> > +                                     "can't set low threshold %d\n", ret);
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +static int veml6030_set_integration_time(struct iio_dev *indio_dev,
> > +                                             int val, int val2)
> > +{
> > +     int ret, int_time;
> > +     struct veml6030_data *data = iio_priv(indio_dev);
> > +
> > +     if (val)
> > +             return -EINVAL;
> > +
> > +     switch (val2) {
> > +     case 25000:
> > +             int_time = 12;
> > +             break;
> > +     case 50000:
> > +             int_time = 8;
> > +             break;
> > +     case 100000:
> > +             int_time = 0;
> > +             break;
> > +     case 200000:
> > +             int_time = 1;
> > +             break;
> > +     case 400000:
> > +             int_time = 2;
> > +             break;
> > +     case 800000:
> > +             int_time = 3;
> > +             break;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +
> > +     ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF,
> > +                                     VEML6030_ALS_IT, int_time);
> > +     if (ret) {
> > +             dev_err(&data->client->dev,
> > +                             "can't update integration time %d\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +/*
> > + * Light reading in lux is obtained by multiplying a constant
> > + * specified in appnote 84367 in the lux calculation table
> > + * to the raw reading.
> > + */
> > +static int veml6030_read_raw(struct iio_dev *indio_dev,
> > +                         struct iio_chan_spec const *chan, int *val,
> > +                         int *val2, long mask)
> > +{
> > +     int ret, rdval;
> > +     struct veml6030_data *data = iio_priv(indio_dev);
> > +     struct regmap *regmap = data->regmap;
> > +
> > +     switch (mask) {
> > +     case IIO_CHAN_INFO_RAW:
> > +             switch (chan->type) {
> > +             case IIO_LIGHT:
> > +                     ret = regmap_read(regmap, VEML6030_REG_ALS_DATA, &rdval);
> > +                     if (ret < 0) {
> > +                             dev_err(&data->client->dev, "can't read ALS data\n");
> > +                             return ret;
> > +                     }
> > +                     *val = rdval;
> > +                     return IIO_VAL_INT;
> > +             case IIO_INTENSITY:
> > +                     ret = regmap_read(regmap, VEML6030_REG_WHITE_DATA, &rdval);
> > +                     if (ret < 0) {
> > +                             dev_err(&data->client->dev, "can't read white data\n");
> > +                             return ret;
> > +                     }
> > +                     *val = rdval;
> > +                     return IIO_VAL_INT;
> > +             default:
> > +                     return -EINVAL;
> > +             }
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +}
> > +
> > +static int veml6030_write_raw(struct iio_dev *indio_dev,
> > +                             struct iio_chan_spec const *chan,
> > +                             int val, int val2, long mask)
> > +{
> > +     switch (mask) {
> > +     case IIO_CHAN_INFO_INT_TIME:
> > +             switch (chan->type) {
> > +             case IIO_LIGHT:
> > +                     return veml6030_set_integration_time(indio_dev, val, val2);
> > +             default:
> > +                     return -EINVAL;
> > +             }
> > +     case IIO_CHAN_INFO_SCALE:
> > +             switch (chan->type) {
> > +             case IIO_LIGHT:
> > +                     return veml6030_set_als_gain(indio_dev, val, val2);
> > +             default:
> > +                     return -EINVAL;
> > +             }
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +}
> > +
> > +static int veml6030_write_thresh(struct iio_dev *indio_dev,
> > +             const struct iio_chan_spec *chan, enum iio_event_type type,
> > +             enum iio_event_direction dir, enum iio_event_info info,
> > +             int val, int val2)
> > +{
> > +     switch (info) {
> > +     case IIO_EV_INFO_VALUE:
> > +             return veml6030_set_als_threshold(indio_dev, val, val2, dir);
> > +     case IIO_EV_INFO_PERIOD:
> > +             return veml6030_set_als_persistence(indio_dev, val, val2);
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +
> > +     return -EINVAL;
> > +}
> > +
> > +static int veml6030_write_interrupt_config(struct iio_dev *indio_dev,
> > +             const struct iio_chan_spec *chan, enum iio_event_type type,
> > +             enum iio_event_direction dir, int state)
> > +{
> > +     int ret;
> > +     struct veml6030_data *data = iio_priv(indio_dev);
> > +
> > +     if (!data->client->irq || state < 0 || state > 1)
> > +             return -EINVAL;
> > +
> > +     ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF,
> > +                                     VEML6030_ALS_INT_EN, state);
> > +     if (ret) {
> > +             dev_err(&data->client->dev,
> > +                             "can't enable threshold interrupt %d\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +static const struct iio_info veml6030_info = {
> > +     .read_raw  = veml6030_read_raw,
> > +     .write_raw = veml6030_write_raw,
> > +     .write_event_value      = veml6030_write_thresh,
> > +     .write_event_config     = veml6030_write_interrupt_config,
> > +     .attrs = &veml6030_attr_group,
> > +};
> > +
> > +static irqreturn_t veml6030_event_handler(int irq, void *private)
> > +{
> > +     int ret, reg, evtdir;
> > +     struct iio_dev *indio_dev = private;
> > +     struct veml6030_data *data = iio_priv(indio_dev);
> > +
> > +     ret = regmap_read(data->regmap, VEML6030_REG_ALS_INT, &reg);
> > +     if (ret) {
> > +             dev_err(&data->client->dev,
> > +                             "can't enable interrupt status %d\n", ret);
> > +             return IRQ_HANDLED;
> > +     }
> > +
> > +     /* Spurious interrupt handling */
> > +     if (!(reg & (VEML6030_INT_TH_HIGH | VEML6030_INT_TH_LOW)))
> > +             return IRQ_HANDLED;
> > +
> > +     if (reg & VEML6030_INT_TH_HIGH)
> > +             evtdir = IIO_EV_DIR_RISING;
> > +     else
> > +             evtdir = IIO_EV_DIR_FALLING;
> > +
> > +     iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_INTENSITY,
> > +                                     0, IIO_EV_TYPE_THRESH, evtdir),
> > +                                     iio_get_time_ns(indio_dev));
> > +
> > +     return IRQ_HANDLED;
> > +}
> > +
> > +/*
> > + * Corresponding to 1200 ms as refresh time, set ALS gain to
> > + * ALS gain x 2, integration time to 200 ms and PSM to mode 2.
> > + * Set persistence to 1 x integration time, and the threashold
> > + * interrupt disabled by default.
> > + */
> > +static int veml6030_hw_init(struct iio_dev *indio_dev)
> > +{
> > +     int ret, val;
> > +     struct veml6030_data *data = iio_priv(indio_dev);
> > +     struct i2c_client *client = data->client;
> > +
> > +     ret = regmap_write(data->regmap, VEML6030_REG_ALS_CONF, 0x850);
> > +     if (ret) {
> > +             dev_err(&client->dev, "can't setup ALS configs %d\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     /* Wait 4 ms to let processor & oscillator start correctly */
> > +     usleep_range(3990, 4000);
> > +
> > +     ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_PSM,
> > +                              VEML6030_PSM | VEML6030_PSM_EN, 0x03);
> > +     if (ret) {
> > +             dev_err(&client->dev, "can't setup defaults for PSM %d\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     ret = regmap_write(data->regmap, VEML6030_REG_ALS_WH, 0xFFFF);
> > +     if (ret) {
> > +             dev_err(&client->dev, "can't read high threshold %d\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     ret = regmap_write(data->regmap, VEML6030_REG_ALS_WL, 0x0000);
> > +     if (ret) {
> > +             dev_err(&client->dev, "can't read low threshold %d\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     /* Clear stale interrupt status bits if any during start */
> > +     ret = regmap_read(data->regmap, VEML6030_REG_ALS_INT, &val);
> > +     if (ret < 0) {
> > +             dev_err(&client->dev,
> > +                     "can't read ALS interrupt status %d\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +static int veml6030_probe(struct i2c_client *client,
> > +                       const struct i2c_device_id *id)
> > +{
> > +     int ret;
> > +     struct veml6030_data *data;
> > +     struct iio_dev *indio_dev;
> > +     struct regmap *regmap;
> > +
> > +     if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
> > +             dev_err(&client->dev, "i2c adapter doesn't support plain i2c\n");
> > +             return -EOPNOTSUPP;
> > +     }
> > +
> > +     regmap = devm_regmap_init_i2c(client, &veml6030_regmap_config);
> > +     if (IS_ERR(regmap)) {
> > +             dev_err(&client->dev, "can't setup regmap\n");
> > +             return PTR_ERR(regmap);
> > +     }
> > +
> > +     indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
> > +     if (!indio_dev)
> > +             return -ENOMEM;
> > +
> > +     data = iio_priv(indio_dev);
> > +     i2c_set_clientdata(client, indio_dev);
> > +     data->client = client;
> > +     data->regmap = regmap;
> > +
> > +     indio_dev->dev.parent = &client->dev;
> > +     indio_dev->info = &veml6030_info;
> > +     indio_dev->name = "veml6030";
> > +     indio_dev->channels = veml6030_channels;
> > +     indio_dev->num_channels = ARRAY_SIZE(veml6030_channels);
> > +     indio_dev->modes = INDIO_DIRECT_MODE;
> > +
> > +     if (client->irq) {
> > +             ret = devm_request_threaded_irq(&client->dev, client->irq,
> > +                                             NULL, veml6030_event_handler,
> > +                                             IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> > +                                             "veml6030", indio_dev);
> > +             if (ret < 0) {
> > +                     dev_err(&client->dev,
> > +                                     "irq %d request failed\n", client->irq);
> > +                     return ret;
> > +             }
> > +     }
> > +
> > +     ret = veml6030_hw_init(indio_dev);
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     return devm_iio_device_register(&client->dev, indio_dev);
> > +}
> > +
> > +static int veml6030_remove(struct i2c_client *client)
> > +{
> > +     struct iio_dev *indio_dev = i2c_get_clientdata(client);
> > +     struct veml6030_data *data = iio_priv(indio_dev);
> > +
> > +     iio_device_unregister(indio_dev);
>
> needed? you are using devm_iio_device_register() above
>
> > +
> > +     regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF,
> > +                              VEML6030_ALS_SD, 0x01);
> > +
> > +     return 0;
> > +}
> > +
> > +#ifdef CONFIG_PM
> > +static int veml6030_runtime_suspend(struct device *dev)
> > +{
> > +     int ret;
> > +     struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> > +     struct veml6030_data *data = iio_priv(indio_dev);
> > +
> > +     ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF,
> > +                              VEML6030_ALS_SD, 0x01);
> > +     if (ret < 0) {
> > +             dev_err(&data->client->dev,
> > +                                     "can't suspend veml6030 light sensor\n");
> > +             return ret;
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +static int veml6030_runtime_resume(struct device *dev)
> > +{
> > +     int ret;
> > +     struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> > +     struct veml6030_data *data = iio_priv(indio_dev);
> > +
> > +     ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF,
> > +                              VEML6030_ALS_SD, 0x00);
> > +     if (ret < 0) {
> > +             dev_err(&data->client->dev,
> > +                                     "can't resume veml6030 light sensor\n");
> > +             return ret;
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +static const struct dev_pm_ops veml6030_pm_ops = {
> > +     SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> > +                             pm_runtime_force_resume)
> > +     SET_RUNTIME_PM_OPS(veml6030_runtime_suspend,
> > +                             veml6030_runtime_resume, NULL)
> > +};
> > +#endif
> > +
> > +static const struct of_device_id veml6030_of_match[] = {
> > +     { .compatible = "vishay,veml6030" },
> > +     { }
> > +};
> > +MODULE_DEVICE_TABLE(of, veml6030_of_match);
> > +
> > +static const struct i2c_device_id veml6030_id[] = {
> > +     { "veml6030", 0 },
> > +     { }
> > +};
> > +MODULE_DEVICE_TABLE(i2c, veml6030_id);
> > +
> > +static struct i2c_driver veml6030_driver = {
> > +     .driver = {
> > +             .name = "veml6030",
> > +             .of_match_table = of_match_ptr(veml6030_of_match),
> > +#ifdef CONFIG_PM
> > +             .pm = &veml6030_pm_ops,
> > +#endif
> > +     },
> > +     .probe = veml6030_probe,
> > +     .remove = veml6030_remove,
> > +     .id_table = veml6030_id,
> > +};
> > +module_i2c_driver(veml6030_driver);
> > +
> > +MODULE_AUTHOR("Rishi Gupta <gupt21@xxxxxxxxx>");
> > +MODULE_DESCRIPTION("VEML6030 Ambient Light Sensor");
> > +MODULE_LICENSE("GPL v2");
> >
>
> --
>
> Peter Meerwald-Stadler
> Mobile: +43 664 24 44 418



[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux