drivers: sensor: tsl2540
Add the tsl2540 sensor to drivers. Signed-off-by: Rick Talbott <richard.talbott1@t-mobile.com>
This commit is contained in:
parent
7e2005b861
commit
a07b79a8bf
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
6
drivers/sensor/tsl2540/CMakeLists.txt
Normal file
6
drivers/sensor/tsl2540/CMakeLists.txt
Normal 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)
|
54
drivers/sensor/tsl2540/Kconfig
Normal file
54
drivers/sensor/tsl2540/Kconfig
Normal 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
|
373
drivers/sensor/tsl2540/tsl2540.c
Normal file
373
drivers/sensor/tsl2540/tsl2540.c
Normal 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)
|
115
drivers/sensor/tsl2540/tsl2540.h
Normal file
115
drivers/sensor/tsl2540/tsl2540.h
Normal 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
|
200
drivers/sensor/tsl2540/tsl2540_trigger.c
Normal file
200
drivers/sensor/tsl2540/tsl2540_trigger.c
Normal 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;
|
||||
}
|
34
dts/bindings/sensor/ams,tsl2540.yaml
Normal file
34
dts/bindings/sensor/ams,tsl2540.yaml
Normal 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
|
52
include/zephyr/drivers/sensor/tsl2540.h
Normal file
52
include/zephyr/drivers/sensor/tsl2540.h
Normal 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_ */
|
|
@ -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>;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue