drivers: sensor: Add VCNL4040 driver

Add support for Vishay VCNL4040 proximity and light sensor

Signed-off-by: Richard Osterloh <richard.osterloh@gmail.com>
This commit is contained in:
Richard Osterloh 2020-04-21 11:21:56 +01:00 committed by Maureen Helm
commit 0a0026afe2
10 changed files with 981 additions and 0 deletions

View file

@ -70,6 +70,7 @@ add_subdirectory_ifdef(CONFIG_TH02 th02)
add_subdirectory_ifdef(CONFIG_TMP007 tmp007)
add_subdirectory_ifdef(CONFIG_TMP112 tmp112)
add_subdirectory_ifdef(CONFIG_TMP116 tmp116)
add_subdirectory_ifdef(CONFIG_VCNL4040 vcnl4040)
add_subdirectory_ifdef(CONFIG_VL53L0X vl53l0x)
add_subdirectory_ifdef(CONFIG_TEMP_KINETIS nxp_kinetis_temp)
add_subdirectory_ifdef(CONFIG_TACH_XEC mchp_tach_xec)

View file

@ -179,6 +179,8 @@ source "drivers/sensor/tmp112/Kconfig"
source "drivers/sensor/tmp116/Kconfig"
source "drivers/sensor/vcnl4040/Kconfig"
source "drivers/sensor/vl53l0x/Kconfig"
source "drivers/sensor/nxp_kinetis_temp/Kconfig"

View file

@ -0,0 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
zephyr_library()
zephyr_library_sources_ifdef(CONFIG_VCNL4040 vcnl4040.c)
zephyr_library_sources_ifdef(CONFIG_VCNL4040_TRIGGER vcnl4040_trigger.c)

View file

@ -0,0 +1,57 @@
# VCNL4040 Proximity and Ambient Light Sensor configuration options
# Copyright (c) 2020 Richard Osterloh
# SPDX-License-Identifier: Apache-2.0
menuconfig VCNL4040
bool "VCNL4040 Proximity and Ambient Light Sensor"
depends on I2C && HAS_DTS_I2C && HAS_DTS_GPIO
help
Enable driver for VCNL4040 sensors.
if VCNL4040
config VCNL4040_ENABLE_ALS
bool "Enable ambient light sensor"
help
Enable Ambient Light Sense (ALS).
config VCNL4040_TRIGGER
bool
choice
prompt "Trigger mode"
default VCNL4040_TRIGGER_NONE
help
Specify the type of triggering to be used by the driver.
config VCNL4040_TRIGGER_NONE
bool "No trigger"
config VCNL4040_TRIGGER_GLOBAL_THREAD
bool "Use global thread"
depends on GPIO
select VCNL4040_TRIGGER
config VCNL4040_TRIGGER_OWN_THREAD
bool "Use own thread"
depends on GPIO
select VCNL4040_TRIGGER
endchoice
config VCNL4040_THREAD_PRIORITY
int "Thread priority"
depends on VCNL4040_TRIGGER_OWN_THREAD
default 10
help
Priority of thread used by the driver to handle interrupts.
config VCNL4040_THREAD_STACK_SIZE
int "Thread stack size"
depends on VCNL4040_TRIGGER_OWN_THREAD
default 1024
help
Stack size of thread used by the driver to handle interrupts.
endif # VCNL4040

View file

@ -0,0 +1,382 @@
/*
* Copyright (c) 2020 Richard Osterloh
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT vishay_vcnl4040
#include "vcnl4040.h"
#include <sys/__assert.h>
#include <sys/byteorder.h>
#include <sys/util.h>
#include <logging/log.h>
#include <stdlib.h>
LOG_MODULE_REGISTER(vcnl4040, CONFIG_SENSOR_LOG_LEVEL);
int vcnl4040_read(const struct device *dev, uint8_t reg, uint16_t *out)
{
const struct vcnl4040_config *config = dev->config;
struct vcnl4040_data *data = dev->data;
uint8_t buff[2] = { 0 };
int ret = 0;
ret = i2c_write_read(data->i2c, config->i2c_address,
&reg, sizeof(reg), buff, sizeof(buff));
if (!ret)
*out = sys_get_le16(buff);
return ret;
}
int vcnl4040_write(const struct device *dev, uint8_t reg, uint16_t value)
{
const struct vcnl4040_config *config = dev->config;
struct vcnl4040_data *data = dev->data;
struct i2c_msg msg;
int ret;
uint8_t buff[3];
sys_put_le16(value, &buff[1]);
buff[0] = reg;
msg.buf = buff;
msg.flags = 0;
msg.len = sizeof(buff);
ret = i2c_transfer(data->i2c, &msg, 1, config->i2c_address);
if (ret < 0) {
LOG_ERR("write block failed");
return ret;
}
return 0;
}
static int vcnl4040_sample_fetch(const struct device *dev,
enum sensor_channel chan)
{
struct vcnl4040_data *data = dev->data;
int ret = 0;
#ifdef CONFIG_VCNL4040_ENABLE_ALS
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL ||
chan == SENSOR_CHAN_PROX ||
chan == SENSOR_CHAN_LIGHT);
#else
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_PROX);
#endif
k_sem_take(&data->sem, K_FOREVER);
if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_PROX) {
ret = vcnl4040_read(dev, VCNL4040_REG_PS_DATA,
&data->proximity);
if (ret < 0) {
LOG_ERR("Could not fetch proximity");
goto exit;
}
}
#ifdef CONFIG_VCNL4040_ENABLE_ALS
if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_LIGHT) {
ret = vcnl4040_read(dev, VCNL4040_REG_ALS_DATA,
&data->light);
if (ret < 0) {
LOG_ERR("Could not fetch ambient light");
}
}
#endif
exit:
k_sem_give(&data->sem);
return ret;
}
static int vcnl4040_channel_get(const struct device *dev,
enum sensor_channel chan,
struct sensor_value *val)
{
struct vcnl4040_data *data = dev->data;
int ret = 0;
k_sem_take(&data->sem, K_FOREVER);
switch (chan) {
case SENSOR_CHAN_PROX:
val->val1 = data->proximity;
val->val2 = 0;
break;
#ifdef CONFIG_VCNL4040_ENABLE_ALS
case SENSOR_CHAN_LIGHT:
val->val1 = data->light * data->sensitivity;
val->val2 = 0;
break;
#endif
default:
ret = -ENOTSUP;
}
k_sem_give(&data->sem);
return ret;
}
static int vcnl4040_proxy_setup(const struct device *dev)
{
const struct vcnl4040_config *config = dev->config;
uint16_t conf = 0;
if (vcnl4040_read(dev, VCNL4040_REG_PS_MS, &conf)) {
LOG_ERR("Could not read proximity config");
return -EIO;
}
/* Set LED current */
conf |= config->led_i << VCNL4040_LED_I_POS;
if (vcnl4040_write(dev, VCNL4040_REG_PS_MS, conf)) {
LOG_ERR("Could not write proximity config");
return -EIO;
}
if (vcnl4040_read(dev, VCNL4040_REG_PS_CONF, &conf)) {
LOG_ERR("Could not read proximity config");
return -EIO;
}
/* Set PS_HD */
conf |= VCNL4040_PS_HD_MASK;
/* Set duty cycle */
conf |= config->led_dc << VCNL4040_PS_DUTY_POS;
/* Set integration time */
conf |= config->proxy_it << VCNL4040_PS_IT_POS;
/* Clear proximity shutdown */
conf &= ~VCNL4040_PS_SD_MASK;
if (vcnl4040_write(dev, VCNL4040_REG_PS_CONF, conf)) {
LOG_ERR("Could not write proximity config");
return -EIO;
}
return 0;
}
#ifdef CONFIG_VCNL4040_ENABLE_ALS
static int vcnl4040_ambient_setup(const struct device *dev)
{
const struct vcnl4040_config *config = dev->config;
struct vcnl4040_data *data = dev->data;
uint16_t conf = 0;
if (vcnl4040_read(dev, VCNL4040_REG_ALS_CONF, &conf)) {
LOG_ERR("Could not read proximity config");
return -EIO;
}
/* Set ALS integration time */
conf |= config->als_it << VCNL4040_ALS_IT_POS;
/* Clear ALS shutdown */
conf &= ~VCNL4040_ALS_SD_MASK;
if (vcnl4040_write(dev, VCNL4040_REG_ALS_CONF, conf)) {
LOG_ERR("Could not write proximity config");
return -EIO;
}
/*
* scale the lux depending on the value of the integration time
* see page 8 of the VCNL4040 application note:
* https://www.vishay.com/docs/84307/designingvcnl4040.pdf
*/
switch (config->als_it) {
case VCNL4040_AMBIENT_INTEGRATION_TIME_80MS:
data->sensitivity = 0.12;
break;
case VCNL4040_AMBIENT_INTEGRATION_TIME_160MS:
data->sensitivity = 0.06;
break;
case VCNL4040_AMBIENT_INTEGRATION_TIME_320MS:
data->sensitivity = 0.03;
break;
case VCNL4040_AMBIENT_INTEGRATION_TIME_640MS:
data->sensitivity = 0.015;
break;
default:
data->sensitivity = 1.0;
LOG_WRN("Cannot set ALS sensitivity from ALS_IT=%d",
config->als_it);
break;
}
return 0;
}
#endif
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
static int vcnl4040_device_ctrl(const struct device *dev,
uint32_t ctrl_command, void *context,
device_pm_cb cb, void *arg)
{
int ret = 0;
if (ctrl_command == DEVICE_PM_SET_POWER_STATE) {
uint32_t device_pm_state = *(uint32_t *)context;
uint16_t ps_conf;
ret = vcnl4040_read(dev, VCNL4040_REG_PS_CONF, &ps_conf);
if (ret < 0)
return ret;
#ifdef CONFIG_VCNL4040_ENABLE_ALS
uint16_t als_conf;
ret = vcnl4040_read(dev, VCNL4040_REG_ALS_CONF, &als_conf);
if (ret < 0)
return ret;
#endif
if (device_pm_state == DEVICE_PM_ACTIVE_STATE) {
/* Clear proximity shutdown */
ps_conf &= ~VCNL4040_PS_SD_MASK;
ret = vcnl4040_write(dev, VCNL4040_REG_PS_CONF,
ps_conf);
if (ret < 0)
return ret;
#ifdef CONFIG_VCNL4040_ENABLE_ALS
/* Clear als shutdown */
als_conf &= ~VCNL4040_ALS_SD_MASK;
ret = vcnl4040_write(dev, VCNL4040_REG_ALS_CONF,
als_conf);
if (ret < 0)
return ret;
#endif
} else {
/* Set proximity shutdown bit 0 */
ps_conf |= VCNL4040_PS_SD_MASK;
ret = vcnl4040_write(dev, VCNL4040_REG_PS_CONF,
ps_conf);
if (ret < 0)
return ret;
#ifdef CONFIG_VCNL4040_ENABLE_ALS
/* Clear als shutdown bit 0 */
als_conf |= VCNL4040_ALS_SD_MASK;
ret = vcnl4040_write(dev, VCNL4040_REG_ALS_CONF,
als_conf)
if (ret < 0)
return ret;
#endif
}
} else if (ctrl_command == DEVICE_PM_GET_POWER_STATE) {
*((uint32_t *)context) = DEVICE_PM_ACTIVE_STATE;
}
if (cb) {
cb(dev, ret, context, arg);
}
return ret;
}
#endif
static int vcnl4040_init(const struct device *dev)
{
const struct vcnl4040_config *config = dev->config;
struct vcnl4040_data *data = dev->data;
uint16_t id;
/* Get the I2C device */
data->i2c = device_get_binding(config->i2c_name);
if (data->i2c == NULL) {
LOG_ERR("Could not find I2C device");
return -EINVAL;
}
/* Check device id */
if (vcnl4040_read(dev, VCNL4040_REG_DEVICE_ID, &id)) {
LOG_ERR("Could not read device id");
return -EIO;
}
if (id != VCNL4040_DEFAULT_ID) {
LOG_ERR("Incorrect device id (%d)", id);
return -EIO;
}
if (vcnl4040_proxy_setup(dev)) {
LOG_ERR("Failed to setup proximity functionality");
return -EIO;
}
#ifdef CONFIG_VCNL4040_ENABLE_ALS
if (vcnl4040_ambient_setup(dev)) {
LOG_ERR("Failed to setup ambient light functionality");
return -EIO;
}
#endif
k_sem_init(&data->sem, 0, UINT_MAX);
#if CONFIG_VCNL4040_TRIGGER
if (vcnl4040_trigger_init(dev)) {
LOG_ERR("Could not initialise interrupts");
return -EIO;
}
#endif
k_sem_give(&data->sem);
LOG_DBG("Init complete");
return 0;
}
static const struct sensor_driver_api vcnl4040_driver_api = {
.sample_fetch = vcnl4040_sample_fetch,
.channel_get = vcnl4040_channel_get,
#ifdef CONFIG_VCNL4040_TRIGGER
.attr_set = vcnl4040_attr_set,
.trigger_set = vcnl4040_trigger_set,
#endif
};
static const struct vcnl4040_config vcnl4040_config = {
.i2c_name = DT_INST_BUS_LABEL(0),
.i2c_address = DT_INST_REG_ADDR(0),
#ifdef CONFIG_VCNL4040_TRIGGER
#if DT_INST_NODE_HAS_PROP(0, int_gpios)
.gpio_name = DT_INST_GPIO_LABEL(0, int_gpios),
.gpio_pin = DT_INST_GPIO_PIN(0, int_gpios),
.gpio_flags = DT_INST_GPIO_FLAGS(0, int_gpios),
#else
.gpio_name = NULL,
.gpio_pin = 0,
.gpio_flags = 0,
#endif
#endif
.led_i = DT_ENUM_IDX(DT_DRV_INST(0), led_current),
.led_dc = DT_ENUM_IDX(DT_DRV_INST(0), led_duty_cycle),
.als_it = DT_ENUM_IDX(DT_DRV_INST(0), als_it),
.proxy_it = DT_ENUM_IDX(DT_DRV_INST(0), proximity_it),
.proxy_type = DT_ENUM_IDX(DT_DRV_INST(0), proximity_trigger),
};
static struct vcnl4040_data vcnl4040_data;
#ifndef CONFIG_DEVICE_POWER_MANAGEMENT
DEVICE_AND_API_INIT(vcnl4040, DT_INST_LABEL(0), vcnl4040_init,
&vcnl4040_data, &vcnl4040_config,
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,
&vcnl4040_driver_api);
#else
DEVICE_DEFINE(vcnl4040, DT_INST_LABEL(0), vcnl4040_init,
vcnl4040_device_ctrl, &vcnl4040_data, &vcnl4040_config,
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &vcnl4040_driver_api);
#endif

View file

@ -0,0 +1,148 @@
/*
* Copyright (c) 2020 Richard Osterloh
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_DRIVERS_SENSOR_VCNL4040_VCNL4040_H_
#define ZEPHYR_DRIVERS_SENSOR_VCNL4040_VCNL4040_H_
#include <drivers/sensor.h>
#include <drivers/i2c.h>
#include <drivers/gpio.h>
/* Registers all 16 bits */
#define VCNL4040_REG_ALS_CONF 0x00
#define VCNL4040_REG_ALS_THDH 0x01
#define VCNL4040_REG_ALS_THDL 0x02
#define VCNL4040_REG_PS_CONF 0x03
#define VCNL4040_REG_PS_MS 0x04
#define VCNL4040_REG_PS_CANC 0x05
#define VCNL4040_REG_PS_THDL 0x06
#define VCNL4040_REG_PS_THDH 0x07
#define VCNL4040_REG_PS_DATA 0x08
#define VCNL4040_REG_ALS_DATA 0x09
#define VCNL4040_REG_WHITE_DATA 0x0A
#define VCNL4040_REG_INT_FLAG 0x0B
#define VCNL4040_REG_DEVICE_ID 0x0C
#define VCNL4040_DEFAULT_ID 0x0186
#define VCNL4040_LED_I_POS 8
#define VCNL4040_PS_HD_POS 11
#define VCNL4040_PS_HD_MASK (0x01 << VCNL4040_PS_HD_POS)
#define VCNL4040_PS_DUTY_POS 6
#define VCNL4040_PS_IT_POS 1
#define VCNL4040_PS_SD_POS 0
#define VCNL4040_PS_SD_MASK (0x01 << VCNL4040_PS_SD_POS)
#define VCNL4040_ALS_IT_POS 6
#define VCNL4040_ALS_SD_POS 0
#define VCNL4040_ALS_SD_MASK (0x01 << VCNL4040_ALS_SD_POS)
enum led_current {
VCNL4040_LED_CURRENT_50MA,
VCNL4040_LED_CURRENT_75MA,
VCNL4040_LED_CURRENT_100MA,
VCNL4040_LED_CURRENT_120MA,
VCNL4040_LED_CURRENT_140MA,
VCNL4040_LED_CURRENT_160MA,
VCNL4040_LED_CURRENT_180MA,
VCNL4040_LED_CURRENT_200MA,
};
enum led_duty_cycle {
VCNL4040_LED_DUTY_1_40,
VCNL4040_LED_DUTY_1_80,
VCNL4040_LED_DUTY_1_160,
VCNL4040_LED_DUTY_1_320,
};
enum ambient_integration_time {
VCNL4040_AMBIENT_INTEGRATION_TIME_80MS,
VCNL4040_AMBIENT_INTEGRATION_TIME_160MS,
VCNL4040_AMBIENT_INTEGRATION_TIME_320MS,
VCNL4040_AMBIENT_INTEGRATION_TIME_640MS,
};
enum proximity_integration_time {
VCNL4040_PROXIMITY_INTEGRATION_TIME_1T,
VCNL4040_PROXIMITY_INTEGRATION_TIME_1_5T,
VCNL4040_PROXIMITY_INTEGRATION_TIME_2T,
VCNL4040_PROXIMITY_INTEGRATION_TIME_2_5T,
VCNL4040_PROXIMITY_INTEGRATION_TIME_3T,
VCNL4040_PROXIMITY_INTEGRATION_TIME_3_5T,
VCNL4040_PROXIMITY_INTEGRATION_TIME_4T,
VCNL4040_PROXIMITY_INTEGRATION_TIME_8T,
};
enum proximity_type {
VCNL4040_PROXIMITY_INT_DISABLE,
VCNL4040_PROXIMITY_INT_CLOSE,
VCNL4040_PROXIMITY_INT_AWAY,
VCNL4040_PROXIMITY_INT_CLOSE_AWAY,
};
enum interrupt_type {
VCNL4040_PROXIMITY_AWAY = 1,
VCNL4040_PROXIMITY_CLOSE,
VCNL4040_AMBIENT_HIGH = 4,
VCNL4040_AMBIENT_LOW,
};
struct vcnl4040_config {
char *i2c_name;
uint8_t i2c_address;
#ifdef CONFIG_VCNL4040_TRIGGER
const char *gpio_name;
gpio_pin_t gpio_pin;
gpio_dt_flags_t gpio_flags;
#endif
enum led_current led_i;
enum led_duty_cycle led_dc;
enum ambient_integration_time als_it;
enum proximity_integration_time proxy_it;
enum proximity_type proxy_type;
};
struct vcnl4040_data {
const struct device *i2c;
struct k_sem sem;
#ifdef CONFIG_VCNL4040_TRIGGER
const struct device *dev;
const struct device *gpio;
uint8_t gpio_pin;
struct gpio_callback gpio_cb;
enum interrupt_type int_type;
sensor_trigger_handler_t proxy_handler;
sensor_trigger_handler_t als_handler;
#endif
#ifdef CONFIG_VCNL4040_TRIGGER_OWN_THREAD
K_THREAD_STACK_MEMBER(thread_stack, CONFIG_VCNL4040_THREAD_STACK_SIZE);
struct k_thread thread;
struct k_sem trig_sem;
#endif
#ifdef CONFIG_VCNL4040_TRIGGER_GLOBAL_THREAD
struct k_work work;
#endif
uint16_t proximity;
uint16_t light;
float sensitivity;
};
int vcnl4040_read(const struct device *dev, uint8_t reg, uint16_t *out);
int vcnl4040_write(const struct device *dev, uint8_t reg, uint16_t value);
#ifdef CONFIG_VCNL4040_TRIGGER
int vcnl4040_trigger_init(const struct device *dev);
int vcnl4040_attr_set(const struct device *dev,
enum sensor_channel chan,
enum sensor_attribute attr,
const struct sensor_value *val);
int vcnl4040_trigger_set(const struct device *dev,
const struct sensor_trigger *trig,
sensor_trigger_handler_t handler);
#endif
#endif

View file

@ -0,0 +1,294 @@
/*
* Copyright (c) 2020 Richard Osterloh
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "vcnl4040.h"
#include <logging/log.h>
LOG_MODULE_DECLARE(vcnl4040, CONFIG_SENSOR_LOG_LEVEL);
static void vcnl4040_handle_cb(struct vcnl4040_data *data)
{
gpio_pin_interrupt_configure(data->gpio, data->gpio_pin,
GPIO_INT_DISABLE);
#if defined(CONFIG_VCNL4040_TRIGGER_OWN_THREAD)
k_sem_give(&data->trig_sem);
#elif defined(CONFIG_VCNL4040_TRIGGER_GLOBAL_THREAD)
k_work_submit(&data->work);
#endif
}
static void vcnl4040_gpio_callback(const struct device *dev,
struct gpio_callback *cb,
uint32_t pin_mask)
{
struct vcnl4040_data *data =
CONTAINER_OF(cb, struct vcnl4040_data, gpio_cb);
if ((pin_mask & BIT(data->gpio_pin)) == 0U) {
return;
}
vcnl4040_handle_cb(data);
}
static int vcnl4040_handle_proxy_int(const struct device *dev)
{
struct vcnl4040_data *data = dev->data;
struct sensor_trigger proxy_trig = {
.type = SENSOR_TRIG_THRESHOLD,
.chan = SENSOR_CHAN_PROX,
};
if (data->proxy_handler) {
data->proxy_handler(dev, &proxy_trig);
}
return 0;
}
static int vcnl4040_handle_als_int(const struct device *dev)
{
struct vcnl4040_data *data = dev->data;
struct sensor_trigger als_trig = {
.type = SENSOR_TRIG_THRESHOLD,
.chan = SENSOR_CHAN_LIGHT,
};
if (data->als_handler) {
data->als_handler(dev, &als_trig);
}
return 0;
}
static void vcnl4040_handle_int(const struct device *dev)
{
const struct vcnl4040_config *config = dev->config;
struct vcnl4040_data *data = dev->data;
uint16_t int_source;
k_sem_take(&data->sem, K_FOREVER);
if (vcnl4040_read(dev, VCNL4040_REG_INT_FLAG, &int_source)) {
LOG_ERR("Could not read interrupt source");
int_source = 0U;
}
k_sem_give(&data->sem);
data->int_type = int_source >> 8;
if (data->int_type == VCNL4040_PROXIMITY_AWAY ||
data->int_type == VCNL4040_PROXIMITY_CLOSE) {
vcnl4040_handle_proxy_int(dev);
} else if (data->int_type == VCNL4040_AMBIENT_HIGH ||
data->int_type == VCNL4040_AMBIENT_LOW) {
vcnl4040_handle_als_int(dev);
} else {
LOG_ERR("Unknown interrupt source %d", data->int_type);
}
gpio_pin_interrupt_configure(data->gpio, config->gpio_pin,
GPIO_INT_EDGE_TO_ACTIVE);
}
#ifdef CONFIG_VCNL4040_TRIGGER_OWN_THREAD
static void vcnl4040_thread_main(struct vcnl4040_data *data)
{
while (true) {
k_sem_take(&data->trig_sem, K_FOREVER);
vcnl4040_handle_int(data->dev);
}
}
#endif
#ifdef CONFIG_VCNL4040_TRIGGER_GLOBAL_THREAD
static void vcnl4040_work_handler(struct k_work *work)
{
struct vcnl4040_data *data =
CONTAINER_OF(work, struct vcnl4040_data, work);
vcnl4040_handle_int(data->dev);
}
#endif
int vcnl4040_attr_set(const struct device *dev,
enum sensor_channel chan,
enum sensor_attribute attr,
const struct sensor_value *val)
{
struct vcnl4040_data *data = dev->data;
int ret = 0;
k_sem_take(&data->sem, K_FOREVER);
if (chan == SENSOR_CHAN_PROX) {
if (attr == SENSOR_ATTR_UPPER_THRESH) {
if (vcnl4040_write(dev, VCNL4040_REG_PS_THDH,
(uint16_t)val->val1)) {
ret = -EIO;
goto exit;
}
ret = 0;
goto exit;
}
if (attr == SENSOR_ATTR_LOWER_THRESH) {
if (vcnl4040_write(dev, VCNL4040_REG_PS_THDL,
(uint16_t)val->val1)) {
ret = -EIO;
goto exit;
}
ret = 0;
goto exit;
}
}
#ifdef CONFIG_VCNL4040_ENABLE_ALS
if (chan == SENSOR_CHAN_LIGHT) {
if (attr == SENSOR_ATTR_UPPER_THRESH) {
if (vcnl4040_write(dev, VCNL4040_REG_ALS_THDH,
(uint16_t)val->val1)) {
ret = -EIO;
goto exit;
}
ret = 0;
goto exit;
}
if (attr == SENSOR_ATTR_LOWER_THRESH) {
if (vcnl4040_write(dev, VCNL4040_REG_ALS_THDL,
(uint16_t)val->val1)) {
ret = -EIO;
goto exit;
}
ret = 0;
goto exit;
}
}
#endif
ret = -ENOTSUP;
exit:
k_sem_give(&data->sem);
return ret;
}
int vcnl4040_trigger_set(const struct device *dev,
const struct sensor_trigger *trig,
sensor_trigger_handler_t handler)
{
const struct vcnl4040_config *config = dev->config;
struct vcnl4040_data *data = dev->data;
uint16_t conf;
int ret = 0;
k_sem_take(&data->sem, K_FOREVER);
switch (trig->type) {
case SENSOR_TRIG_THRESHOLD:
if (trig->chan == SENSOR_CHAN_PROX) {
if (vcnl4040_read(dev, VCNL4040_REG_PS_CONF, &conf)) {
ret = -EIO;
goto exit;
}
/* Set interrupt bits 1:0 of high register */
conf |= config->proxy_type << 8;
if (vcnl4040_write(dev, VCNL4040_REG_PS_CONF, conf)) {
ret = -EIO;
goto exit;
}
data->proxy_handler = handler;
} else if (trig->chan == SENSOR_CHAN_LIGHT) {
#ifdef CONFIG_VCNL4040_ENABLE_ALS
if (vcnl4040_read(dev, VCNL4040_REG_ALS_CONF, &conf)) {
ret = -EIO;
goto exit;
}
/* Set interrupt enable bit 1 */
conf |= 1 << 1;
if (vcnl4040_write(dev, VCNL4040_REG_ALS_CONF, conf)) {
ret = -EIO;
goto exit;
}
data->als_handler = handler;
#else
ret = -ENOTSUP;
goto exit;
#endif
} else {
ret = -ENOTSUP;
goto exit;
}
break;
default:
LOG_ERR("Unsupported sensor trigger");
ret = -ENOTSUP;
goto exit;
}
exit:
k_sem_give(&data->sem);
return ret;
}
int vcnl4040_trigger_init(const struct device *dev)
{
const struct vcnl4040_config *config = dev->config;
struct vcnl4040_data *data = dev->data;
data->dev = dev;
#if defined(CONFIG_VCNL4040_TRIGGER_OWN_THREAD)
k_sem_init(&data->trig_sem, 0, UINT_MAX);
k_thread_create(&data->thread, data->thread_stack,
CONFIG_VCNL4040_THREAD_STACK_SIZE,
(k_thread_entry_t)vcnl4040_thread_main,
data, NULL, NULL,
K_PRIO_COOP(CONFIG_VCNL4040_THREAD_PRIORITY),
0, K_NO_WAIT);
k_thread_name_set(&data->thread, "VCNL4040 trigger");
#elif defined(CONFIG_VCNL4040_TRIGGER_GLOBAL_THREAD)
data->work.handler = vcnl4040_work_handler;
#endif
/* Get the GPIO device */
data->gpio = device_get_binding(config->gpio_name);
if (data->gpio == NULL) {
LOG_ERR("Could not find GPIO device");
return -EINVAL;
}
data->gpio_pin = config->gpio_pin;
gpio_pin_configure(data->gpio, config->gpio_pin,
GPIO_INPUT | config->gpio_flags);
gpio_init_callback(&data->gpio_cb, vcnl4040_gpio_callback,
BIT(config->gpio_pin));
if (gpio_add_callback(data->gpio, &data->gpio_cb) < 0) {
LOG_DBG("Failed to set gpio callback!");
return -EIO;
}
gpio_pin_interrupt_configure(data->gpio, config->gpio_pin,
GPIO_INT_EDGE_TO_ACTIVE);
if (gpio_pin_get(data->gpio, config->gpio_pin) > 0) {
vcnl4040_handle_cb(data);
}
return 0;
}

View file

@ -0,0 +1,81 @@
# Copyright (c) 2020 Richard Osterloh
# SPDX-License-Identifier: Apache-2.0
description: VCNL4040 proximity and ambient light sensor
compatible: "vishay,vcnl4040"
include: i2c-device.yaml
properties:
int-gpios:
type: phandle-array
required: false
description: |
The INT pin signals that a programmable interrupt function
for ALS and PS with upper and lower thresholds has been
triggered. The sensor generates an active-low level signal
which remains asserted until the data is read.
led-current:
type: int
required: false
default: 50
description: LED current in mA
enum:
- 50
- 75
- 100
- 120
- 140
- 160
- 180
- 200
led-duty-cycle:
type: int
required: false
default: 40
description: LED duty cycle in Hz
enum:
- 40
- 80
- 160
- 320
proximity-it:
type: string
required: false
default: "1"
description: Proximity integration time in T
enum:
- "1"
- "1.5"
- "2"
- "2.5"
- "3"
- "3.5"
- "4"
- "8"
proximity-trigger:
type: string
required: false
default: "close-away"
description: Proximity trigger type
enum:
- "disabled"
- "close"
- "away"
- "close-away"
als-it:
type: int
required: false
default: 80
description: ALS integration time in ms
enum:
- 80
- 160
- 320
- 640

View file

@ -559,3 +559,10 @@ test_i2c_max17055: max17055@49 {
desired-charging-current = <2000>;
rsense-mohms = <5>;
};
test_i2c_vcnl4040: vcnl4040@60 {
compatible = "vishay,vcnl4040";
label = "VCNL4040";
reg = <0x60>;
int-gpios = <&test_gpio 0 0>;
};

View file

@ -1,6 +1,7 @@
CONFIG_MAX_THREAD_BYTES=4
CONFIG_TEST=y
CONFIG_TEST_USERSPACE=y
CONFIG_MAX_THREAD_BYTES=3
CONFIG_I2C=y
CONFIG_ADC=y
CONFIG_GPIO=y
@ -36,3 +37,5 @@ CONFIG_SX9500_TRIGGER_OWN_THREAD=y
CONFIG_TMP007=y
CONFIG_TMP007_TRIGGER_OWN_THREAD=y
CONFIG_ITDS=y
CONFIG_VCNL4040=y
CONFIG_VCNL4040_TRIGGER_OWN_THREAD=y