drivers: sensor: tsl2540

Add the tsl2540 sensor to drivers.

Signed-off-by: Rick Talbott <richard.talbott1@t-mobile.com>
This commit is contained in:
Rick Talbott 2023-09-08 10:41:02 -06:00 committed by Fabio Baltieri
parent 7e2005b861
commit a07b79a8bf
13 changed files with 845 additions and 0 deletions

View file

@ -137,6 +137,7 @@ add_subdirectory_ifdef(CONFIG_TMP007 tmp007)
add_subdirectory_ifdef(CONFIG_TMP108 tmp108)
add_subdirectory_ifdef(CONFIG_TMP112 tmp112)
add_subdirectory_ifdef(CONFIG_TMP116 tmp116)
add_subdirectory_ifdef(CONFIG_TSL2540 tsl2540)
add_subdirectory_ifdef(CONFIG_VCMP_IT8XXX2 ite_vcmp_it8xxx2)
add_subdirectory_ifdef(CONFIG_VCNL4040 vcnl4040)
add_subdirectory_ifdef(CONFIG_VEML7700 veml7700)

View file

@ -193,6 +193,7 @@ source "drivers/sensor/tmp007/Kconfig"
source "drivers/sensor/tmp108/Kconfig"
source "drivers/sensor/tmp112/Kconfig"
source "drivers/sensor/tmp116/Kconfig"
source "drivers/sensor/tsl2540/Kconfig"
source "drivers/sensor/vcnl4040/Kconfig"
source "drivers/sensor/veml7700/Kconfig"
source "drivers/sensor/vl53l0x/Kconfig"

View file

@ -0,0 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
zephyr_library()
zephyr_library_sources(tsl2540.c)
zephyr_library_sources_ifdef(CONFIG_TSL2540_TRIGGER tsl2540_trigger.c)

View file

@ -0,0 +1,54 @@
# TSL2540 Ambient Light Sensor configuration options
# Copyright (c) 2022 T-Mobile USA, Inc.
# SPDX-License-Identifier: Apache-2.0
menuconfig TSL2540
bool "TSL2540 Ambient Light Sensor"
default y
depends on DT_HAS_AMS_TSL2540_ENABLED
select I2C
help
Enable driver for TSL2540 sensors.
if TSL2540
config TSL2540_TRIGGER
bool
choice
prompt "Trigger mode"
default TSL2540_TRIGGER_NONE
help
Specify the type of triggering to be used by the driver.
config TSL2540_TRIGGER_NONE
bool "No trigger"
config TSL2540_TRIGGER_GLOBAL_THREAD
bool "Use global thread"
depends on GPIO
select TSL2540_TRIGGER
config TSL2540_TRIGGER_OWN_THREAD
bool "Use own thread"
depends on GPIO
select TSL2540_TRIGGER
endchoice
config TSL2540_THREAD_PRIORITY
int "Thread priority"
depends on TSL2540_TRIGGER_OWN_THREAD
default 10
help
Priority of thread used by the driver to handle interrupts.
config TSL2540_THREAD_STACK_SIZE
int "Thread stack size"
depends on TSL2540_TRIGGER_OWN_THREAD
default 1024
help
Stack size of thread used by the driver to handle interrupts.
endif # TSL2540

View file

@ -0,0 +1,373 @@
/*
* Copyright (c) 2022 T-Mobile USA, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT ams_tsl2540
#include "tsl2540.h"
#include <stdlib.h>
#include <zephyr/logging/log.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/pm/device.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/util.h>
#define TSL2540_INTEGRATION_TIME_MS (2.81)
#define TSL2540_DEVICE_FACTOR (53.0)
#define FIXED_ATTENUATION_TO_DBL(x) (x * 0.00001)
LOG_MODULE_REGISTER(tsl2540, CONFIG_SENSOR_LOG_LEVEL);
static int tsl2540_sample_fetch(const struct device *dev, enum sensor_channel chan)
{
const struct tsl2540_config *cfg = dev->config;
struct tsl2540_data *data = dev->data;
int ret = 0;
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_LIGHT ||
chan == SENSOR_CHAN_IR);
k_sem_take(&data->sem, K_FOREVER);
if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_LIGHT) {
uint16_t le16_buffer;
ret = i2c_burst_read_dt(&cfg->i2c_spec, TSL2540_REG_VIS_LOW,
(uint8_t *)&le16_buffer, sizeof(le16_buffer));
if (ret) {
LOG_ERR("Could not fetch ambient light (visible)");
k_sem_give(&data->sem);
return -EIO;
}
data->count_vis = sys_le16_to_cpu(le16_buffer);
}
if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_IR) {
uint16_t le16_buffer;
ret = i2c_burst_read_dt(&cfg->i2c_spec, TSL2540_REG_IR_LOW, (uint8_t *)&le16_buffer,
sizeof(le16_buffer));
if (ret) {
LOG_ERR("Could not fetch ambient light (IR)");
k_sem_give(&data->sem);
return -EIO;
}
data->count_ir = sys_le16_to_cpu(le16_buffer);
}
k_sem_give(&data->sem);
return ret;
}
static int tsl2540_channel_get(const struct device *dev, enum sensor_channel chan,
struct sensor_value *val)
{
const struct tsl2540_config *cfg = dev->config;
struct tsl2540_data *data = dev->data;
int ret = 0;
double cpl;
double glass_attenuation = FIXED_ATTENUATION_TO_DBL(cfg->glass_attenuation);
double glass_ir_attenuation = FIXED_ATTENUATION_TO_DBL(cfg->glass_ir_attenuation);
k_sem_take(&data->sem, K_FOREVER);
cpl = (data->integration_time + 1) * TSL2540_INTEGRATION_TIME_MS;
cpl *= data->again;
switch (chan) {
case SENSOR_CHAN_LIGHT:
sensor_value_from_double(val, data->count_vis / cpl *
TSL2540_DEVICE_FACTOR * glass_attenuation);
break;
case SENSOR_CHAN_IR:
sensor_value_from_double(val, data->count_ir / cpl *
TSL2540_DEVICE_FACTOR * glass_ir_attenuation);
break;
default:
ret = -ENOTSUP;
}
k_sem_give(&data->sem);
return ret;
}
static int tsl2540_attr_set_gain(const struct device *dev, enum sensor_gain_tsl2540 gain)
{
const struct tsl2540_config *cfg = dev->config;
struct tsl2540_data *data = dev->data;
uint8_t value = 0;
double again = 0.0;
switch (gain) {
case TSL2540_SENSOR_GAIN_1_2:
value = TSL2540_CFG1_G1_2;
again = TSL2540_AGAIN_S1_2;
break;
case TSL2540_SENSOR_GAIN_1:
value = TSL2540_CFG1_G1;
again = TSL2540_AGAIN_S1;
break;
case TSL2540_SENSOR_GAIN_4:
value = TSL2540_CFG1_G4;
again = TSL2540_AGAIN_S4;
break;
case TSL2540_SENSOR_GAIN_16:
value = TSL2540_CFG1_G16;
again = TSL2540_AGAIN_S16;
break;
case TSL2540_SENSOR_GAIN_64:
value = TSL2540_CFG1_G64;
again = TSL2540_AGAIN_S64;
break;
case TSL2540_SENSOR_GAIN_128:
value = TSL2540_CFG1_G128;
again = TSL2540_CFG2_G128;
break;
}
if (i2c_reg_write_byte_dt(&cfg->i2c_spec, TSL2540_REG_CFG_1, value) < 0) {
return -EIO;
}
if (i2c_reg_write_byte_dt(&cfg->i2c_spec, TSL2540_REG_CFG_2, value) < 0) {
return -EIO;
}
data->again = again;
return 0;
}
static int tsl2540_attr_set(const struct device *dev, enum sensor_channel chan,
enum sensor_attribute attr, const struct sensor_value *val)
{
const struct tsl2540_config *cfg = dev->config;
struct tsl2540_data *data = dev->data;
int ret = 0;
uint8_t temp;
double it;
if ((chan != SENSOR_CHAN_IR) & (chan != SENSOR_CHAN_LIGHT)) {
return -ENOTSUP;
}
k_sem_take(&data->sem, K_FOREVER);
i2c_reg_write_byte_dt(&cfg->i2c_spec, TSL2540_ENABLE_ADDR, TSL2540_ENABLE_MASK &
~TSL2540_ENABLE_CONF);
#if CONFIG_TSL2540_TRIGGER
if (chan == SENSOR_CHAN_LIGHT) {
if (attr == SENSOR_ATTR_UPPER_THRESH) {
double cpl;
uint16_t thld, le16_buffer;
double glass_attenuation = FIXED_ATTENUATION_TO_DBL(cfg->glass_attenuation);
cpl = ((data->integration_time + 1) * TSL2540_INTEGRATION_TIME_MS);
cpl *= data->again;
cpl /= (TSL2540_DEVICE_FACTOR * glass_attenuation);
thld = sensor_value_to_double(val) * cpl;
LOG_DBG("attr: %d, cpl: %g, thld: %x\n", attr, cpl, thld);
le16_buffer = sys_cpu_to_le16(thld);
ret = i2c_burst_write_dt(
&((const struct tsl2540_config *)dev->config)->i2c_spec,
TSL2540_REG_AIHT_LOW, (uint8_t *)&le16_buffer, sizeof(le16_buffer));
goto exit;
}
if (attr == SENSOR_ATTR_LOWER_THRESH) {
double cpl;
uint16_t thld, le16_buffer;
double glass_attenuation = FIXED_ATTENUATION_TO_DBL(cfg->glass_attenuation);
cpl = ((data->integration_time + 1) * TSL2540_INTEGRATION_TIME_MS);
cpl *= data->again;
cpl /= (TSL2540_DEVICE_FACTOR * glass_attenuation);
thld = sensor_value_to_double(val) * cpl;
LOG_DBG("attr: %d, cpl: %g, thld: %x\n", attr, cpl, thld);
le16_buffer = sys_cpu_to_le16(sys_cpu_to_le16(thld));
ret = i2c_burst_write_dt(
&((const struct tsl2540_config *)dev->config)->i2c_spec,
TSL2540_REG_AILT_LOW, (uint8_t *)&le16_buffer, sizeof(le16_buffer));
goto exit;
}
}
#endif /* CONFIG_TSL2540_TRIGGER */
switch ((enum sensor_attribute_tsl2540)attr) {
case SENSOR_ATTR_GAIN:
tsl2540_attr_set_gain(dev, (enum sensor_gain_tsl2540)val->val1);
break;
case SENSOR_ATTR_INT_APERS:
temp = (uint8_t)val->val1;
if (temp > 15) {
ret = -EINVAL;
goto exit;
}
if (i2c_reg_write_byte_dt(&cfg->i2c_spec, TSL2540_REG_PERS, temp)) {
ret = -EIO;
goto exit;
}
break;
case SENSOR_ATTR_INTEGRATION_TIME:
it = sensor_value_to_double(val);
it /= TSL2540_INTEGRATION_TIME_MS;
if (it < 1 || it > 256) {
ret = -EINVAL;
goto exit;
}
it -= 1;
temp = (uint8_t)it;
if (i2c_reg_write_byte_dt(&cfg->i2c_spec, TSL2540_REG_ATIME, temp)) {
ret = -EIO;
goto exit;
}
data->integration_time = temp;
ret = 0;
break;
case SENSOR_ATTR_TSL2540_SHUTDOWN_MODE:
data->enable_mode = TSL2540_ENABLE_DISABLE;
ret = i2c_reg_update_byte_dt(&cfg->i2c_spec, TSL2540_CFG3_ADDR, TSL2540_CFG3_MASK,
TSL2540_CFG3_CONF);
break;
case SENSOR_ATTR_TSL2540_CONTINUOUS_MODE:
data->enable_mode = TSL2540_ENABLE_CONF;
ret = i2c_reg_update_byte_dt(&cfg->i2c_spec, TSL2540_CFG3_ADDR, TSL2540_CFG3_MASK,
TSL2540_CFG3_CONF);
break;
case SENSOR_ATTR_TSL2540_CONTINUOUS_NO_WAIT_MODE:
data->enable_mode = TSL2540_ENABLE_AEN_PON;
ret = i2c_reg_update_byte_dt(&cfg->i2c_spec, TSL2540_CFG3_ADDR, TSL2540_CFG3_MASK,
TSL2540_CFG3_DFLT);
break;
}
exit:
i2c_reg_update_byte_dt(&cfg->i2c_spec, TSL2540_ENABLE_ADDR, TSL2540_ENABLE_MASK,
data->enable_mode);
k_sem_give(&data->sem);
return ret;
}
static int tsl2540_setup(const struct device *dev)
{
struct sensor_value integration_time;
/* Set ALS integration time */
tsl2540_attr_set(dev, (enum sensor_channel)SENSOR_CHAN_LIGHT,
(enum sensor_attribute)SENSOR_ATTR_GAIN,
&(struct sensor_value){.val1 = TSL2540_SENSOR_GAIN_1_2, .val2 = 0});
sensor_value_from_double(&integration_time, 500.0);
tsl2540_attr_set(dev, (enum sensor_channel)SENSOR_CHAN_LIGHT,
(enum sensor_attribute)SENSOR_ATTR_INTEGRATION_TIME, &integration_time);
return 0;
}
static int tsl2540_init(const struct device *dev)
{
const struct tsl2540_config *cfg = dev->config;
struct tsl2540_data *data = dev->data;
data->enable_mode = TSL2540_ENABLE_DISABLE;
k_sem_init(&data->sem, 1, K_SEM_MAX_LIMIT);
if (!i2c_is_ready_dt(&cfg->i2c_spec)) {
LOG_ERR("I2C dev %s not ready", cfg->i2c_spec.bus->name);
return -ENODEV;
}
i2c_reg_write_byte_dt(&cfg->i2c_spec, TSL2540_REG_PERS, 1);
i2c_reg_update_byte_dt(&cfg->i2c_spec, TSL2540_CFG3_ADDR, TSL2540_CFG3_MASK,
TSL2540_CFG3_DFLT);
if (tsl2540_setup(dev)) {
LOG_ERR("Failed to setup ambient light functionality");
return -EIO;
}
#if CONFIG_TSL2540_TRIGGER
if (tsl2540_trigger_init(dev)) {
LOG_ERR("Could not initialize interrupts");
return -EIO;
}
#endif
LOG_DBG("Init complete");
return 0;
}
static const struct sensor_driver_api tsl2540_driver_api = {
.sample_fetch = tsl2540_sample_fetch,
.channel_get = tsl2540_channel_get,
.attr_set = tsl2540_attr_set,
#ifdef CONFIG_TSL2540_TRIGGER
.trigger_set = tsl2540_trigger_set,
#endif
};
#ifdef CONFIG_PM_DEVICE
static int tsl2540_pm_action(const struct device *dev, enum pm_device_action action)
{
const struct tsl2540_config *cfg = dev->config;
struct tsl2540_data *data = dev->data;
int ret = 0;
switch (action) {
case PM_DEVICE_ACTION_RESUME:
ret = i2c_reg_update_byte_dt(&cfg->i2c_spec, TSL2540_ENABLE_ADDR,
TSL2540_ENABLE_MASK, data->enable_mode);
break;
case PM_DEVICE_ACTION_SUSPEND:
ret = i2c_reg_update_byte_dt(&cfg->i2c_spec, TSL2540_ENABLE_ADDR,
TSL2540_ENABLE_MASK, TSL2540_ENABLE_DISABLE);
break;
default:
return -ENOTSUP;
}
return ret;
}
#endif
#define TSL2540_GLASS_ATTEN(inst) \
.glass_attenuation = DT_INST_PROP(inst, glass_attenuation), \
.glass_ir_attenuation = DT_INST_PROP(inst, glass_ir_attenuation), \
#define TSL2540_DEFINE(inst) \
static struct tsl2540_data tsl2540_prv_data_##inst; \
static const struct tsl2540_config tsl2540_config_##inst = { \
.i2c_spec = I2C_DT_SPEC_INST_GET(inst), \
IF_ENABLED(CONFIG_TSL2540_TRIGGER, \
(.int_gpio = GPIO_DT_SPEC_INST_GET(inst, int_gpios),)) \
TSL2540_GLASS_ATTEN(inst) \
}; \
PM_DEVICE_DT_INST_DEFINE(inst, tsl2540_pm_action); \
SENSOR_DEVICE_DT_INST_DEFINE(inst, &tsl2540_init, PM_DEVICE_DT_INST_GET(inst), \
&tsl2540_prv_data_##inst, &tsl2540_config_##inst, POST_KERNEL, \
CONFIG_SENSOR_INIT_PRIORITY, &tsl2540_driver_api);
DT_INST_FOREACH_STATUS_OKAY(TSL2540_DEFINE)

View file

@ -0,0 +1,115 @@
/*
* Copyright (c) 2022 T-Mobile USA, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_DRIVERS_SENSOR_TSL2540_TSL2540_H_
#define ZEPHYR_DRIVERS_SENSOR_TSL2540_TSL2540_H_
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/drivers/sensor/tsl2540.h>
#define TSL2540_REG_ATIME 0x81
#define TSL2540_REG_WTIME 0x83
#define TSL2540_REG_AILT_LOW 0x84
#define TSL2540_REG_AILT_HI 0x85
#define TSL2540_REG_AIHT_LOW 0x86
#define TSL2540_REG_AIHT_HI 0x87
#define TSL2540_REG_PERS 0x8c
#define TSL2540_REG_CFG_0 0x8d
#define TSL2540_REG_CFG_1 0x90
#define TSL2540_REG_REVID 0x91
#define TSL2540_REG_ID 0x92
#define TSL2540_REG_STATUS 0x93
#define TSL2540_REG_VIS_LOW 0x94
#define TSL2540_REG_VIS_HI 0x95
#define TSL2540_REG_IR_LOW 0x96
#define TSL2540_REG_IR_HI 0x97
#define TSL2540_REG_REVID2 0x9E
#define TSL2540_REG_CFG_2 0x9f
#define TSL2540_AGAIN_S1_2 0.5
#define TSL2540_AGAIN_S1 1
#define TSL2540_AGAIN_S4 4
#define TSL2540_AGAIN_S16 16
#define TSL2540_AGAIN_S64 67
#define TSL2540_AGAIN_S128 140
#define TSL2540_CFG1_G1_2 0x00
#define TSL2540_CFG1_G1 0x00
#define TSL2540_CFG1_G4 0x01
#define TSL2540_CFG1_G16 0x02
#define TSL2540_CFG1_G64 0x03
#define TSL2540_CFG1_G128 0x03
#define TSL2540_CFG2_G1_2 0x00
#define TSL2540_CFG2_G1 0x04
#define TSL2540_CFG2_G4 0x04
#define TSL2540_CFG2_G16 0x04
#define TSL2540_CFG2_G64 0x04
#define TSL2540_CFG2_G128 0x14
/* ENABLE(0x80: 0x00): Reserved:7:4 | WEN:3 | Reserved:2 | AEN:1 | PON:0 */
#define TSL2540_ENABLE_ADDR 0x80
#define TSL2540_ENABLE_MASK (BIT(3) | BIT(1) | BIT(0))
#define TSL2540_ENABLE_CONF (BIT(3) | BIT(1) | BIT(0))
#define TSL2540_ENABLE_AEN_PON (BIT(1) | BIT(0))
#define TSL2540_ENABLE_DISABLE (0)
/* CRG3(0xAB: 0x0C): INT_READ_CLEAR:7 | Reserved:6:5 | SAI:4 | Reserved:3:0 */
#define TSL2540_CFG3_ADDR 0xAB
#define TSL2540_CFG3_MASK (BIT(7) | BIT(4))
#define TSL2540_CFG3_CONF (BIT(7) | BIT(4))
#define TSL2540_CFG3_DFLT (0)
/* INTENAB(0xDD: 0x00): ASIEN:7 | Reserved:6:5 | AIEN:4 | Reserved:3:0 */
#define TSL2540_INTENAB_ADDR 0xDD
#define TSL2540_INTENAB_MASK (BIT(7) | BIT(4))
#define TSL2540_INTENAB_CONF (BIT(4))
#define TSL2540_INT_EN_AEN 0x90
struct tsl2540_config {
const struct i2c_dt_spec i2c_spec;
#ifdef CONFIG_TSL2540_TRIGGER
const struct gpio_dt_spec int_gpio;
#endif
const uint32_t glass_attenuation;
const uint32_t glass_ir_attenuation;
};
struct tsl2540_data {
const struct device *i2c;
struct k_sem sem;
#ifdef CONFIG_TSL2540_TRIGGER
const struct device *dev;
struct gpio_callback gpio_cb;
const struct sensor_trigger *als_trigger;
sensor_trigger_handler_t als_handler;
#endif
#ifdef CONFIG_TSL2540_TRIGGER_OWN_THREAD
K_THREAD_STACK_MEMBER(thread_stack, CONFIG_TSL2540_THREAD_STACK_SIZE);
struct k_thread thread;
struct k_sem trig_sem;
#endif
#ifdef CONFIG_TSL2540_TRIGGER_GLOBAL_THREAD
struct k_work work;
#endif
uint8_t enable_mode;
uint16_t count_vis;
uint16_t count_ir;
uint8_t integration_time;
double again;
};
#ifdef CONFIG_TSL2540_TRIGGER
int tsl2540_trigger_init(const struct device *dev);
int tsl2540_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
sensor_trigger_handler_t handler);
#endif
#endif

View file

@ -0,0 +1,200 @@
/*
* Copyright (c) 2022 T-Mobile USA, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "tsl2540.h"
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(tsl2540, CONFIG_SENSOR_LOG_LEVEL);
static void tsl2540_setup_int(const struct device *dev, bool enable)
{
const struct tsl2540_config *config = dev->config;
gpio_flags_t flags = enable
? GPIO_INT_EDGE_TO_ACTIVE
: GPIO_INT_DISABLE;
gpio_pin_interrupt_configure_dt(&config->int_gpio, flags);
}
static void tsl2540_handle_int(const struct device *dev)
{
struct tsl2540_data *drv_data = dev->data;
tsl2540_setup_int(dev, false);
#if defined(CONFIG_TSL2540_TRIGGER_OWN_THREAD)
k_sem_give(&drv_data->trig_sem);
#elif defined(CONFIG_TSL2540_TRIGGER_GLOBAL_THREAD)
k_work_submit(&drv_data->work);
#endif
}
static void tsl2540_gpio_callback(const struct device *dev, struct gpio_callback *cb,
uint32_t pin_mask)
{
struct tsl2540_data *data = CONTAINER_OF(cb, struct tsl2540_data, gpio_cb);
tsl2540_handle_int(data->dev);
}
static void tsl2540_process_int(const struct device *dev)
{
const struct tsl2540_config *config = dev->config;
struct tsl2540_data *data = dev->data;
uint8_t status;
/* Read the status, cleared automatically in CFG3 */
int ret = i2c_reg_read_byte_dt(&config->i2c_spec, TSL2540_REG_STATUS, &status);
if (ret) {
LOG_ERR("Could not read status register (%#x), errno: %d", TSL2540_REG_STATUS,
ret);
return;
}
if (BIT(7) & status) { /* ASAT */
LOG_ERR("Interrupt status(%#x): %#x: ASAT", TSL2540_REG_STATUS, status);
}
if (BIT(3) & status) { /* CINT */
LOG_DBG("Interrupt status(%#x): %#x: CINT", TSL2540_REG_STATUS, status);
}
if (BIT(4) & status) { /* AINT */
LOG_DBG("Interrupt status(%#x): %#x: AINT", TSL2540_REG_STATUS, status);
if (data->als_handler != NULL) {
data->als_handler(dev, data->als_trigger);
}
}
tsl2540_setup_int(dev, true);
/* Check for pin that may be asserted while we were busy */
int pv = gpio_pin_get_dt(&config->int_gpio);
if (pv > 0) {
tsl2540_handle_int(dev);
}
}
#ifdef CONFIG_TSL2540_TRIGGER_OWN_THREAD
static void tsl2540_thread_main(struct tsl2540_data *data)
{
while (true) {
k_sem_take(&data->trig_sem, K_FOREVER);
tsl2540_process_int(data->dev);
}
}
#endif
#ifdef CONFIG_TSL2540_TRIGGER_GLOBAL_THREAD
static void tsl2540_work_handler(struct k_work *work)
{
struct tsl2540_data *data = CONTAINER_OF(work, struct tsl2540_data, work);
tsl2540_process_int(data->dev);
}
#endif
int tsl2540_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
sensor_trigger_handler_t handler)
{
const struct tsl2540_config *config = dev->config;
struct tsl2540_data *data = dev->data;
int ret;
if (trig->type != SENSOR_TRIG_THRESHOLD) {
LOG_ERR("Unsupported sensor trigger type: %d", trig->type);
return -ENOTSUP;
}
if (trig->chan != SENSOR_CHAN_LIGHT) {
LOG_ERR("Unsupported sensor trigger channel: %d", trig->chan);
return -ENOTSUP;
}
const struct i2c_dt_spec *i2c_spec = &config->i2c_spec;
ret = i2c_reg_update_byte_dt(i2c_spec, TSL2540_INTENAB_ADDR,
TSL2540_INTENAB_MASK, TSL2540_INTENAB_CONF);
if (ret) {
LOG_ERR("%#x: I/O error: %d", TSL2540_INTENAB_ADDR, ret);
return -EIO;
}
ret = i2c_reg_update_byte_dt(i2c_spec, TSL2540_CFG3_ADDR,
TSL2540_CFG3_MASK, TSL2540_CFG3_CONF);
if (ret) {
LOG_ERR("%#x: I/O error: %d", TSL2540_CFG3_ADDR, ret);
return -EIO;
}
k_sem_take(&data->sem, K_FOREVER);
data->als_handler = handler;
data->als_trigger = trig;
if (handler != NULL) {
tsl2540_setup_int(dev, true);
/* Check whether already asserted */
int pv = gpio_pin_get_dt(&config->int_gpio);
if (pv > 0) {
tsl2540_handle_int(dev);
}
}
k_sem_give(&data->sem);
return ret;
}
int tsl2540_trigger_init(const struct device *dev)
{
const struct tsl2540_config *config = dev->config;
struct tsl2540_data *data = dev->data;
int rc;
/* Check device is defined */
if (config->int_gpio.port == NULL) {
LOG_ERR("int-gpios is not defined in the device tree.");
return -EINVAL;
}
/* Get the GPIO device */
if (!gpio_is_ready_dt(&config->int_gpio)) {
LOG_ERR("%s: gpio controller %s not ready", dev->name, config->int_gpio.port->name);
return -ENODEV;
}
rc = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT);
if (rc < 0) {
return rc;
}
gpio_init_callback(&data->gpio_cb, tsl2540_gpio_callback, BIT(config->int_gpio.pin));
if (gpio_add_callback(config->int_gpio.port, &data->gpio_cb) < 0) {
LOG_ERR("Failed to set gpio callback!");
return -EIO;
}
data->dev = dev;
#if defined(CONFIG_TSL2540_TRIGGER_OWN_THREAD)
k_sem_init(&data->trig_sem, 0, K_SEM_MAX_LIMIT);
k_thread_create(&data->thread, data->thread_stack, CONFIG_TSL2540_THREAD_STACK_SIZE,
(k_thread_entry_t)tsl2540_thread_main, data, NULL, NULL,
K_PRIO_COOP(CONFIG_TSL2540_THREAD_PRIORITY), 0, K_NO_WAIT);
k_thread_name_set(&data->thread, "TSL2540 trigger");
#elif defined(CONFIG_TSL2540_TRIGGER_GLOBAL_THREAD)
data->work.handler = tsl2540_work_handler;
#endif
return 0;
}

View file

@ -0,0 +1,34 @@
# Copyright (c) 2022 T-Mobile USA, Inc.
# SPDX-License-Identifier: Apache-2.0
description: |
TSL2540 series ambient light sensor. See datasheet at
https://ams.com/documents/20143/36005/TSL2540_DS000564_4-00.pdf/39728ac4-098c-9eca-b5ca-61d9c6f3a588
compatible: "ams,tsl2540"
include: [sensor-device.yaml, i2c-device.yaml]
properties:
int-gpios:
type: phandle-array
description: |
Identifies the interrupt pin
glass-attenuation:
type: int
default: 100000
description: |
Visible light attenuation.
Integer value for a represenation with 5 decimal points.
This default value (1.00000) is chosen for free open space (no glass).
Example: 1.2 would be 120000
glass-ir-attenuation:
type: int
default: 100000
description: |
Infa-red light attenuation.
Integer value for a represenation with 5 decimal points.
This default value (1.00000) is chosen for free open space (no glass).
Example: 1.2 would be 120000

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2022 T-Mobile USA, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Extended public API for AMS's TSL2540 ambient light sensor
*
* This exposes attributes for the TSL2540 which can be used for
* setting the on-chip gain and integration time parameters.
*/
#ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_TSL2540_H_
#define ZEPHYR_INCLUDE_DRIVERS_SENSOR_TSL2540_H_
#include <zephyr/drivers/sensor.h>
#ifdef __cplusplus
extern "C" {
#endif
enum sensor_attribute_tsl2540 {
/* Sensor Gain */
SENSOR_ATTR_GAIN = SENSOR_ATTR_PRIV_START + 1,
/* Sensor Integration Time (in ms) */
SENSOR_ATTR_INTEGRATION_TIME,
/* Sensor ALS interrupt persistence filters */
SENSOR_ATTR_INT_APERS,
/* Shutdown the sensor */
SENSOR_ATTR_TSL2540_SHUTDOWN_MODE,
/* Turn on continuous conversion */
SENSOR_ATTR_TSL2540_CONTINUOUS_MODE,
/* Turn on continuous conversion without wait */
SENSOR_ATTR_TSL2540_CONTINUOUS_NO_WAIT_MODE,
};
enum sensor_gain_tsl2540 {
TSL2540_SENSOR_GAIN_1_2,
TSL2540_SENSOR_GAIN_1,
TSL2540_SENSOR_GAIN_4,
TSL2540_SENSOR_GAIN_16,
TSL2540_SENSOR_GAIN_64,
TSL2540_SENSOR_GAIN_128,
};
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_DRIVERS_SENSOR_TSL2540_H_ */

View file

@ -745,3 +745,9 @@ test_i2c_f75303: f75303@70 {
compatible = "fintek,f75303";
reg = <0x70>;
};
test_i2c_tsl2540: tsl2540@71 {
compatible = "ams,tsl2540";
reg = <0x71>;
int-gpios = <&test_gpio 0 0>;
};

View file

@ -49,6 +49,7 @@ CONFIG_SX9500_TRIGGER_GLOBAL_THREAD=y
CONFIG_TCN75A_TRIGGER_GLOBAL_THREAD=y
CONFIG_TMD2620_TRIGGER_GLOBAL_THREAD=y
CONFIG_TMP007_TRIGGER_GLOBAL_THREAD=y
CONFIG_TSL2540_TRIGGER_GLOBAL_THREAD=y
CONFIG_VCNL4040_TRIGGER_GLOBAL_THREAD=y
CONFIG_WSEN_HIDS_TRIGGER_GLOBAL_THREAD=y
CONFIG_WSEN_TIDS_TRIGGER_GLOBAL_THREAD=y

View file

@ -49,6 +49,7 @@ CONFIG_SX9500_TRIGGER_NONE=y
CONFIG_TCN75A_TRIGGER_NONE=y
CONFIG_TMD2620_TRIGGER_NONE=y
CONFIG_TMP007_TRIGGER_NONE=y
CONFIG_TSL2540_TRIGGER_NONE=y
CONFIG_VCNL4040_TRIGGER_NONE=y
CONFIG_WSEN_HIDS_TRIGGER_NONE=y
CONFIG_WSEN_TIDS_TRIGGER_NONE=y

View file

@ -46,6 +46,7 @@ CONFIG_STTS751_TRIGGER_OWN_THREAD=y
CONFIG_SX9500_TRIGGER_OWN_THREAD=y
CONFIG_TCN75A_TRIGGER_OWN_THREAD=y
CONFIG_TMP007_TRIGGER_OWN_THREAD=y
CONFIG_TSL2540_TRIGGER_OWN_THREAD=y
CONFIG_VCNL4040_TRIGGER_OWN_THREAD=y
CONFIG_WSEN_HIDS_TRIGGER_OWN_THREAD=y
CONFIG_WSEN_TIDS_TRIGGER_OWN_THREAD=y