Skip to content

Commit

Permalink
iio: light: vcnl4000: Add interrupt support for vcnl4040
Browse files Browse the repository at this point in the history
Add support to configure proximity sensor interrupts and threshold
limits for vcnl4040. If an interrupt is detected an event will be
pushed to the event interface.

Signed-off-by: Mårten Lindahl <marten.lindahl@axis.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Link: https://lore.kernel.org/r/20230117190017.3789181-4-marten.lindahl@axis.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
  • Loading branch information
martenli authored and jic23 committed Jan 21, 2023
1 parent bfb6cfe commit 5466761
Showing 1 changed file with 169 additions and 0 deletions.
169 changes: 169 additions & 0 deletions drivers/iio/light/vcnl4000.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,11 @@

#define VCNL4200_AL_CONF 0x00 /* Ambient light configuration */
#define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */
#define VCNL4040_PS_THDL_LM 0x06 /* Proximity threshold low */
#define VCNL4040_PS_THDH_LM 0x07 /* Proximity threshold high */
#define VCNL4200_PS_DATA 0x08 /* Proximity data */
#define VCNL4200_AL_DATA 0x09 /* Ambient light data */
#define VCNL4040_INT_FLAGS 0x0b /* Interrupt register */
#define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */

#define VCNL4040_DEV_ID 0x0c /* Device ID and version */
Expand All @@ -78,6 +81,9 @@
#define VCNL4040_ALS_CONF_ALS_SHUTDOWN BIT(0)
#define VCNL4040_PS_CONF1_PS_SHUTDOWN BIT(0)
#define VCNL4040_PS_CONF2_PS_IT GENMASK(3, 1) /* Proximity integration time */
#define VCNL4040_PS_CONF2_PS_INT GENMASK(9, 8) /* Proximity interrupt mode */
#define VCNL4040_PS_IF_AWAY BIT(8) /* Proximity event cross low threshold */
#define VCNL4040_PS_IF_CLOSE BIT(9) /* Proximity event cross high threshold */

/* Bit masks for interrupt registers. */
#define VCNL4010_INT_THR_SEL BIT(0) /* Select threshold interrupt source */
Expand Down Expand Up @@ -138,6 +144,7 @@ struct vcnl4000_data {
enum vcnl4000_device_ids id;
int rev;
int al_scale;
u8 ps_int; /* proximity interrupt mode */
const struct vcnl4000_chip_spec *chip_spec;
struct mutex vcnl4000_lock;
struct vcnl4200_channel vcnl4200_al;
Expand Down Expand Up @@ -256,6 +263,10 @@ static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on)
{
int ret;

/* Do not power down if interrupts are enabled */
if (!on && data->ps_int)
return 0;

ret = vcnl4000_write_als_enable(data, on);
if (ret < 0)
return ret;
Expand Down Expand Up @@ -297,6 +308,7 @@ static int vcnl4200_init(struct vcnl4000_data *data)
dev_dbg(&data->client->dev, "device id 0x%x", id);

data->rev = (ret >> 8) & 0xf;
data->ps_int = 0;

data->vcnl4200_al.reg = VCNL4200_AL_DATA;
data->vcnl4200_ps.reg = VCNL4200_PS_DATA;
Expand Down Expand Up @@ -797,6 +809,64 @@ static int vcnl4010_write_event(struct iio_dev *indio_dev,
}
}

static int vcnl4040_read_event(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)
{
int ret;
struct vcnl4000_data *data = iio_priv(indio_dev);

switch (dir) {
case IIO_EV_DIR_RISING:
ret = i2c_smbus_read_word_data(data->client,
VCNL4040_PS_THDH_LM);
if (ret < 0)
return ret;
*val = ret;
return IIO_VAL_INT;
case IIO_EV_DIR_FALLING:
ret = i2c_smbus_read_word_data(data->client,
VCNL4040_PS_THDL_LM);
if (ret < 0)
return ret;
*val = ret;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}

static int vcnl4040_write_event(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)
{
int ret;
struct vcnl4000_data *data = iio_priv(indio_dev);

switch (dir) {
case IIO_EV_DIR_RISING:
ret = i2c_smbus_write_word_data(data->client,
VCNL4040_PS_THDH_LM, val);
if (ret < 0)
return ret;
return IIO_VAL_INT;
case IIO_EV_DIR_FALLING:
ret = i2c_smbus_write_word_data(data->client,
VCNL4040_PS_THDL_LM, val);
if (ret < 0)
return ret;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}

static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data)
{
int ret;
Expand Down Expand Up @@ -879,6 +949,86 @@ static int vcnl4010_write_event_config(struct iio_dev *indio_dev,
}
}

static int vcnl4040_read_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir)
{
int ret;
struct vcnl4000_data *data = iio_priv(indio_dev);

ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
if (ret < 0)
return ret;

data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret);

return (dir == IIO_EV_DIR_RISING) ?
FIELD_GET(VCNL4040_PS_IF_AWAY, ret) :
FIELD_GET(VCNL4040_PS_IF_CLOSE, ret);
}

static int vcnl4040_write_event_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;
u16 val, mask;
struct vcnl4000_data *data = iio_priv(indio_dev);

mutex_lock(&data->vcnl4000_lock);

ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
if (ret < 0)
goto out;

if (dir == IIO_EV_DIR_RISING)
mask = VCNL4040_PS_IF_AWAY;
else
mask = VCNL4040_PS_IF_CLOSE;

val = state ? (ret | mask) : (ret & ~mask);

data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val);
ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val);

out:
mutex_unlock(&data->vcnl4000_lock);
data->chip_spec->set_power_state(data, data->ps_int != 0);

return ret;
}

static irqreturn_t vcnl4040_irq_thread(int irq, void *p)
{
struct iio_dev *indio_dev = p;
struct vcnl4000_data *data = iio_priv(indio_dev);
int ret;

ret = i2c_smbus_read_word_data(data->client, VCNL4040_INT_FLAGS);
if (ret < 0)
return IRQ_HANDLED;

if (ret & VCNL4040_PS_IF_CLOSE) {
iio_push_event(indio_dev,
IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_RISING),
iio_get_time_ns(indio_dev));
}

if (ret & VCNL4040_PS_IF_AWAY) {
iio_push_event(indio_dev,
IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_FALLING),
iio_get_time_ns(indio_dev));
}

return IRQ_HANDLED;
}

static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev,
uintptr_t priv,
const struct iio_chan_spec *chan,
Expand Down Expand Up @@ -1042,6 +1192,18 @@ static const struct iio_event_spec vcnl4000_event_spec[] = {
}
};

static const struct iio_event_spec vcnl4040_event_spec[] = {
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_RISING,
.mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE),
}, {
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_FALLING,
.mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE),
},
};

static const struct iio_chan_spec vcnl4000_channels[] = {
{
.type = IIO_LIGHT,
Expand Down Expand Up @@ -1090,6 +1252,8 @@ static const struct iio_chan_spec vcnl4040_channels[] = {
BIT(IIO_CHAN_INFO_INT_TIME),
.info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME),
.ext_info = vcnl4000_ext_info,
.event_spec = vcnl4040_event_spec,
.num_event_specs = ARRAY_SIZE(vcnl4040_event_spec),
}
};

Expand All @@ -1110,6 +1274,10 @@ static const struct iio_info vcnl4010_info = {
static const struct iio_info vcnl4040_info = {
.read_raw = vcnl4000_read_raw,
.write_raw = vcnl4040_write_raw,
.read_event_value = vcnl4040_read_event,
.write_event_value = vcnl4040_write_event,
.read_event_config = vcnl4040_read_event_config,
.write_event_config = vcnl4040_write_event_config,
.read_avail = vcnl4040_read_avail,
};

Expand Down Expand Up @@ -1146,6 +1314,7 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
.channels = vcnl4040_channels,
.num_channels = ARRAY_SIZE(vcnl4040_channels),
.info = &vcnl4040_info,
.irq_thread = vcnl4040_irq_thread,
},
[VCNL4200] = {
.prod = "VCNL4200",
Expand Down

0 comments on commit 5466761

Please sign in to comment.