From 5feddf4ebc4525340dabde1e718f54d831f8ba65 Mon Sep 17 00:00:00 2001 From: Rick Talbott Date: Tue, 27 Jun 2023 17:24:04 -0600 Subject: [PATCH] drivers: sensor: tsl2540 Add the tsl2540 sensor to drivers. Signed-off-by: Rick Talbott updated Signed-off-by: RIck Talbott fix Signed-off-by: Rick Talbott --- drivers/sensor/CMakeLists.txt | 1 + drivers/sensor/Kconfig | 1 + drivers/sensor/tsl2540/CMakeLists.txt | 6 + drivers/sensor/tsl2540/Kconfig | 54 +++ drivers/sensor/tsl2540/tsl2540.c | 393 ++++++++++++++++++ drivers/sensor/tsl2540/tsl2540.h | 112 +++++ drivers/sensor/tsl2540/tsl2540_trigger.c | 198 +++++++++ dts/bindings/sensor/ams,tsl2540.yaml | 17 + include/zephyr/drivers/sensor/tsl2540.h | 47 +++ tests/drivers/build_all/sensor/i2c.dtsi | 6 + tests/drivers/build_all/sensor/prj.conf | 1 + .../sensor/sensors_trigger_global.conf | 1 + .../sensor/sensors_trigger_none.conf | 1 + .../build_all/sensor/sensors_trigger_own.conf | 1 + 14 files changed, 839 insertions(+) create mode 100644 drivers/sensor/tsl2540/CMakeLists.txt create mode 100644 drivers/sensor/tsl2540/Kconfig create mode 100644 drivers/sensor/tsl2540/tsl2540.c create mode 100644 drivers/sensor/tsl2540/tsl2540.h create mode 100644 drivers/sensor/tsl2540/tsl2540_trigger.c create mode 100644 dts/bindings/sensor/ams,tsl2540.yaml create mode 100644 include/zephyr/drivers/sensor/tsl2540.h diff --git a/drivers/sensor/CMakeLists.txt b/drivers/sensor/CMakeLists.txt index 4aac5ac9f2ed2b0..f0a5761e0bbf8b2 100644 --- a/drivers/sensor/CMakeLists.txt +++ b/drivers/sensor/CMakeLists.txt @@ -132,6 +132,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) diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig index a91ea8824bd0c8b..e3f2df77a626e61 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -188,6 +188,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" diff --git a/drivers/sensor/tsl2540/CMakeLists.txt b/drivers/sensor/tsl2540/CMakeLists.txt new file mode 100644 index 000000000000000..dbc6ed789275b14 --- /dev/null +++ b/drivers/sensor/tsl2540/CMakeLists.txt @@ -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) diff --git a/drivers/sensor/tsl2540/Kconfig b/drivers/sensor/tsl2540/Kconfig new file mode 100644 index 000000000000000..b4d3e1ec5106457 --- /dev/null +++ b/drivers/sensor/tsl2540/Kconfig @@ -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 diff --git a/drivers/sensor/tsl2540/tsl2540.c b/drivers/sensor/tsl2540/tsl2540.c new file mode 100644 index 000000000000000..a36a54da5980418 --- /dev/null +++ b/drivers/sensor/tsl2540/tsl2540.c @@ -0,0 +1,393 @@ +/* + * Copyright (c) 2022 T-Mobile USA, Inc. + * Copyright (c) 2023 T-Mobile USA, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ams_tsl2540 + +#include "tsl2540.h" + +#include + +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(tsl2540, CONFIG_SENSOR_LOG_LEVEL); + +int tsl2540_reg_read(const struct device *dev, uint8_t reg, uint8_t *val) +{ + const struct tsl2540_config *cfg = dev->config; + int result; + + result = i2c_reg_read_byte_dt(&cfg->i2c_spec, reg, val); + + if (result < 0) { + return result; + } + + return 0; +} + +int tsl2540_reg_write(const struct device *dev, uint8_t reg, uint8_t val) +{ + const struct tsl2540_config *cfg = dev->config; + int result; + + result = i2c_reg_write_byte_dt(&cfg->i2c_spec, reg, val); + + if (result < 0) { + return result; + } + + return 0; +} + +static int tsl2540_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + 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( + &((const struct tsl2540_config *)dev->config)->i2c_spec, + TSL2540_REG_VIS_LOW, (uint8_t *)&le16_buffer, sizeof(le16_buffer)); + if (ret) { + LOG_ERR("Could not fetch ambient light (visible)"); + } else { + 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( + &((const struct tsl2540_config *)dev->config)->i2c_spec, + TSL2540_REG_IR_LOW, (uint8_t *)&le16_buffer, sizeof(le16_buffer)); + if (ret) { + LOG_ERR("Could not fetch ambient light (IR)"); + } else { + 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) +{ + struct tsl2540_data *data = dev->data; + int ret = 0; + double cpl; + + k_sem_take(&data->sem, K_FOREVER); + + cpl = (data->integration_time + 1) * 2.81; + cpl *= data->again; + + switch (chan) { + case SENSOR_CHAN_LIGHT: + sensor_value_from_double(val, + data->count_vis / cpl * 53.0 * data->glass_attenuation); + break; + case SENSOR_CHAN_IR: + sensor_value_from_double(val, + data->count_ir / cpl * 53.0 * data->glass_attenuation_ir); + break; + default: + ret = -ENOTSUP; + } + + k_sem_give(&data->sem); + + return ret; +} + +int tsl2540_attr_set(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, + const struct sensor_value *val) +{ + struct tsl2540_data *data = dev->data; + int ret = 0; + + k_sem_take(&data->sem, K_FOREVER); + tsl2540_reg_write(dev, 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; + + cpl = ((data->integration_time + 1) * 2.81); + cpl *= data->again; + cpl /= (53 * data->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; + + cpl = ((data->integration_time + 1) * 2.81); + cpl *= data->again; + cpl /= (53 * data->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; + } + if ((enum sensor_attribute_tsl2540)attr == SENSOR_ATTR_GLASS_ATTENUATION) { + data->glass_attenuation = sensor_value_to_double(val); + ret = 0; + goto exit; + } + } + if (chan == SENSOR_CHAN_IR) { + if ((enum sensor_attribute_tsl2540)attr == SENSOR_ATTR_GLASS_ATTENUATION) { + data->glass_attenuation = sensor_value_to_double(val); + ret = 0; + goto exit; + } + } +#endif + if (chan == SENSOR_CHAN_IR || chan == SENSOR_CHAN_LIGHT) { + if (attr == (enum sensor_attribute)SENSOR_ATTR_GAIN) { + switch (val->val1) { + case TSL2540_SENSOR_GAIN_1_2: + if (tsl2540_reg_write(dev, TSL2540_REG_CFG_1, TSL2540_CFG1_G1_2)) { + ret = -EIO; + goto exit; + } + if (tsl2540_reg_write(dev, TSL2540_REG_CFG_2, TSL2540_CFG2_G1_2)) { + ret = -EIO; + goto exit; + } + data->again = TSL2540_AGAIN_S1_2; + ret = 0; + goto exit; + case TSL2540_SENSOR_GAIN_1: + if (tsl2540_reg_write(dev, TSL2540_REG_CFG_1, TSL2540_CFG1_G1)) { + ret = -EIO; + goto exit; + } + if (tsl2540_reg_write(dev, TSL2540_REG_CFG_2, TSL2540_CFG2_G1)) { + ret = -EIO; + goto exit; + } + data->again = TSL2540_AGAIN_S1; + ret = 0; + goto exit; + case TSL2540_SENSOR_GAIN_4: + if (tsl2540_reg_write(dev, TSL2540_REG_CFG_1, TSL2540_CFG1_G4)) { + ret = -EIO; + goto exit; + } + if (tsl2540_reg_write(dev, TSL2540_REG_CFG_2, TSL2540_CFG2_G4)) { + ret = -EIO; + goto exit; + } + data->again = TSL2540_AGAIN_S4; + ret = 0; + goto exit; + case TSL2540_SENSOR_GAIN_16: + if (tsl2540_reg_write(dev, TSL2540_REG_CFG_1, TSL2540_CFG1_G16)) { + ret = -EIO; + goto exit; + } + if (tsl2540_reg_write(dev, TSL2540_REG_CFG_2, TSL2540_CFG2_G16)) { + ret = -EIO; + goto exit; + } + data->again = TSL2540_AGAIN_S16; + ret = 0; + goto exit; + case TSL2540_SENSOR_GAIN_64: + if (tsl2540_reg_write(dev, TSL2540_REG_CFG_1, TSL2540_CFG1_G64)) { + ret = -EIO; + goto exit; + } + if (tsl2540_reg_write(dev, TSL2540_REG_CFG_2, TSL2540_CFG2_G64)) { + ret = -EIO; + goto exit; + } + data->again = TSL2540_AGAIN_S64; + ret = 0; + goto exit; + case TSL2540_SENSOR_GAIN_128: + if (tsl2540_reg_write(dev, TSL2540_REG_CFG_1, TSL2540_CFG1_G128)) { + ret = -EIO; + goto exit; + } + if (tsl2540_reg_write(dev, TSL2540_REG_CFG_2, TSL2540_CFG2_G128)) { + ret = -EIO; + goto exit; + } + data->again = TSL2540_AGAIN_S128; + ret = 0; + goto exit; + default: + ret = -EINVAL; + goto exit; + } + if (attr == (enum sensor_attribute)SENSOR_ATTR_INT_APERS) { + uint8_t apv = (uint8_t)val->val1; + + if (apv > 15) { + ret = -EINVAL; + goto exit; + } + if (tsl2540_reg_write(dev, TSL2540_REG_PERS, apv)) { + ret = -EIO; + goto exit; + } + } + } + if (attr == (enum sensor_attribute)SENSOR_ATTR_INTEGRATION_TIME) { + uint8_t itv; + double it; + + it = sensor_value_to_double(val); + it /= 2.81; + if (it < 1 || it > 256) { + ret = -EINVAL; + goto exit; + } + it -= 1; + itv = (uint8_t)it; + if (tsl2540_reg_write(dev, TSL2540_REG_ATIME, itv)) { + ret = -EIO; + goto exit; + } + + data->integration_time = itv; + ret = 0; + goto exit; + } + } + + ret = -ENOTSUP; +exit: + tsl2540_reg_write(dev, TSL2540_ENABLE_ADDR, TSL2540_ENABLE_MASK & TSL2540_ENABLE_CONF); + + 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; + + k_sem_init(&data->sem, 1, K_SEM_MAX_LIMIT); + + /** Calculated from datasheet typical CPL */ + data->glass_attenuation = 2.27205; + data->glass_attenuation_ir = 2.34305; + + tsl2540_reg_write(dev, TSL2540_REG_PERS, 1); + + if (!device_is_ready(cfg->i2c_spec.bus)) { + LOG_ERR("I2C dev %s not ready", cfg->i2c_spec.bus->name); + return -ENODEV; + } + + 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) +{ + int ret = 0; + + switch (action) { + case PM_DEVICE_ACTION_RESUME: + ret = tsl2540_reg_write(dev, TSL2540_ENABLE_ADDR, + TSL2540_ENABLE_MASK & TSL2540_ENABLE_CONF); + break; + case PM_DEVICE_ACTION_SUSPEND: + ret = tsl2540_reg_write(dev, TSL2540_ENABLE_ADDR, + TSL2540_ENABLE_MASK & ~TSL2540_ENABLE_CONF); + break; + default: + return -ENOTSUP; + } + + return ret; +} +#endif + +#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), \ + .int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, {0})}; \ + PM_DEVICE_DT_INST_DEFINE(inst, tsl2540_pm_action); \ + 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) diff --git a/drivers/sensor/tsl2540/tsl2540.h b/drivers/sensor/tsl2540/tsl2540.h new file mode 100644 index 000000000000000..2f988ff74a9c220 --- /dev/null +++ b/drivers/sensor/tsl2540/tsl2540.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2022 T-Mobile USA, Inc. + * Copyright (c) 2023 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 +#include +#include +#include + +#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 ((0000 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (1 << 0)) +#define TSL2540_ENABLE_CONF ((0000 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (1 << 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 ((1 << 7) | (00 << 5) | (1 << 4) | (0000 << 0)) +#define TSL2540_CFG3_CONF ((0 << 7) | (00 << 5) | (1 << 4) | (0000 << 0)) + +/* INTENAB(0xDD: 0x00): ASIEN:7 | Reserved:6:5 | AIEN:4 | Reserved:3:0 */ +#define TSL2540_INTENAB_ADDR 0xDD +#define TSL2540_INTENAB_MASK ((1 << 7) | (00 < 5) | (1 << 4) | (0000 << 0)) +#define TSL2540_INTENAB_CONF ((0 << 7) | (00 < 5) | (1 << 4) | (0000 << 0)) + +#define TSL2540_INT_EN_AEN 0x90 + +struct tsl2540_config { + const struct i2c_dt_spec i2c_spec; + const struct gpio_dt_spec int_gpio; +}; + +struct tsl2540_data { + const struct device *i2c; + struct k_sem sem; +#ifdef CONFIG_TSL2540_TRIGGER + const struct device *dev; + struct gpio_callback gpio_cb; + 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 + uint16_t count_vis; + uint16_t count_ir; + double glass_attenuation; + double glass_attenuation_ir; + uint8_t integration_time; + double again; +}; + +int tsl2540_reg_read(const struct device *dev, uint8_t reg, uint8_t *val); +int tsl2540_reg_write(const struct device *dev, uint8_t reg, uint8_t val); + +#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 diff --git a/drivers/sensor/tsl2540/tsl2540_trigger.c b/drivers/sensor/tsl2540/tsl2540_trigger.c new file mode 100644 index 000000000000000..7948f38207b6fcb --- /dev/null +++ b/drivers/sensor/tsl2540/tsl2540_trigger.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2022 T-Mobile USA, Inc. + * Copyright (c) 2023 T-Mobile USA, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "tsl2540.h" +#include + +LOG_MODULE_DECLARE(tsl2540, CONFIG_SENSOR_LOG_LEVEL); + +static void tsl2540_handle_cb(struct tsl2540_data *data) +{ +#if defined(CONFIG_TSL2540_TRIGGER_OWN_THREAD) + k_sem_give(&data->trig_sem); +#elif defined(CONFIG_TSL2540_TRIGGER_GLOBAL_THREAD) + k_work_submit(&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); + const struct tsl2540_config *config = data->dev->config; + + if ((pin_mask & BIT(config->int_gpio.pin)) == 0U) { + return; + } + + tsl2540_handle_cb(data); +} + +static void tsl2540_handle_int(const struct device *dev) +{ + int ret; + uint8_t status; + + ret = tsl2540_reg_read(dev, TSL2540_REG_STATUS, &status); + if (ret) { + LOG_ERR("Could not read status register (%#x), errno: %d", TSL2540_REG_STATUS, ret); + } else { + if ((1 << 7) & status) { /* ASAT */ + /* + * TODO: + * Implement a mechanism triggered by the over-saturation of one or + * both input amplifiers to automatically adjust the input gain to + * keep the amplifiers from saturating. + */ + + LOG_ERR("Interrupt status(%#x): %#x: ASAT", TSL2540_REG_STATUS, status); + } + if ((1 << 3) & status) { /* CINT */ + LOG_INF("Interrupt status(%#x): %#x: CINT", TSL2540_REG_STATUS, status); + } + if ((1 << 4) & status) { /* AINT */ + struct tsl2540_data *data = dev->data; + + LOG_INF("Interrupt status(%#x): %#x: AINT", TSL2540_REG_STATUS, status); + if (data->als_handler) { + /* + * TODO: Consider fetching the illuminance data and making it + * available to the application; for example: + * + * k_sem_take(&data->sem, K_FOREVER); + * ... + * k_sem_give(&data->sem); + */ + + data->als_handler( + dev, &(struct sensor_trigger){.type = SENSOR_TRIG_THRESHOLD, + .chan = SENSOR_CHAN_LIGHT}); + } + tsl2540_reg_write(dev, TSL2540_REG_STATUS, status); + } + } +} + +#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_handle_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_handle_int(data->dev); +} +#endif + +int tsl2540_trigger_set(const struct device *dev, const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) +{ + int ret; + + if (trig->chan == SENSOR_CHAN_LIGHT) { + switch (trig->type) { + case SENSOR_TRIG_THRESHOLD: { + const struct i2c_dt_spec *i2c_spec = + &((const struct tsl2540_config *)dev->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); + } else { + 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); + } else { + struct tsl2540_data *data = dev->data; + + k_sem_take(&data->sem, K_FOREVER); + data->als_handler = handler; + k_sem_give(&data->sem); + } + } + } break; + + default: + ret = -ENOTSUP; + LOG_ERR("Unsupported sensor trigger type: %d", trig->type); + break; + } + } else { + ret = -ENOTSUP; + LOG_ERR("Unsupported sensor trigger channel: %d", trig->chan); + } + + return ret; +} + +int tsl2540_trigger_init(const struct device *dev) +{ + const struct tsl2540_config *config = dev->config; + struct tsl2540_data *data = dev->data; + + 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 + + /* Get the GPIO device */ + if (!device_is_ready(config->int_gpio.port)) { + LOG_ERR("%s: gpio controller %s not ready", dev->name, config->int_gpio.port->name); + return -ENODEV; + } + + gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT); + + 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_DBG("Failed to set gpio callback!"); + return -EIO; + } + +#define INTERRUPT_MODE_WORKAROUND 1 +#if (0 == INTERRUPT_MODE_WORKAROUND) + /* + * This appears to work; however, the failing-edge/interrupt could be missed + */ + gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_EDGE_TO_ACTIVE); +#elif (1 == INTERRUPT_MODE_WORKAROUND) + /* + * This appears to work; however, an edge/interrupt could be missed and the ISR is called + * ~2x more than should be necessary + */ + gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_EDGE_BOTH); +#else + /* + * This should work, but doesn't. TODO: determine cause + */ + gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_LEVEL_ACTIVE); +#endif + + if (gpio_pin_get_dt(&config->int_gpio) > 0) { + tsl2540_handle_cb(data); + } + + return 0; +} diff --git a/dts/bindings/sensor/ams,tsl2540.yaml b/dts/bindings/sensor/ams,tsl2540.yaml new file mode 100644 index 000000000000000..6b64bfdb30c453b --- /dev/null +++ b/dts/bindings/sensor/ams,tsl2540.yaml @@ -0,0 +1,17 @@ +# 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: i2c-device.yaml + +properties: + int-gpios: + type: phandle-array + required: false + description: | + Identifies the interrupt pin diff --git a/include/zephyr/drivers/sensor/tsl2540.h b/include/zephyr/drivers/sensor/tsl2540.h new file mode 100644 index 000000000000000..d7e6a5fa6eb2692 --- /dev/null +++ b/include/zephyr/drivers/sensor/tsl2540.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 T-Mobile USA, Inc. + * Copyright (c) 2023 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_ + +#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 Glass Attenuation Factor */ + SENSOR_ATTR_GLASS_ATTENUATION, + /** Sensor ALS interrupt persistence filters */ + SENSOR_ATTR_INT_APERS, +}; + +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_ */ diff --git a/tests/drivers/build_all/sensor/i2c.dtsi b/tests/drivers/build_all/sensor/i2c.dtsi index d7bc380c92b78cd..df7e2c589730b23 100644 --- a/tests/drivers/build_all/sensor/i2c.dtsi +++ b/tests/drivers/build_all/sensor/i2c.dtsi @@ -745,3 +745,9 @@ test_tcn75a: tcn75a@73 { reg = <0x73>; alert-gpios = <&test_gpio 0 0>; }; + +test_i2c_tsl2540: tsl2540@74 { + compatible = "ams,tsl2540"; + reg = <0x74>; + int-gpios = <&test_gpio 0 0>; +}; diff --git a/tests/drivers/build_all/sensor/prj.conf b/tests/drivers/build_all/sensor/prj.conf index b29c11a64bb25b1..e54974c0fbbe720 100644 --- a/tests/drivers/build_all/sensor/prj.conf +++ b/tests/drivers/build_all/sensor/prj.conf @@ -11,3 +11,4 @@ CONFIG_SPI=y CONFIG_W1=y CONFIG_SENSOR=y CONFIG_ICM42605_TRIGGER_NONE=y +CONFIG_TSL2540=y diff --git a/tests/drivers/build_all/sensor/sensors_trigger_global.conf b/tests/drivers/build_all/sensor/sensors_trigger_global.conf index 28b1663f1bce2c9..565e66c0e6d5b37 100644 --- a/tests/drivers/build_all/sensor/sensors_trigger_global.conf +++ b/tests/drivers/build_all/sensor/sensors_trigger_global.conf @@ -33,6 +33,7 @@ CONFIG_LIS2DW12_TRIGGER_GLOBAL_THREAD=y CONFIG_LIS2MDL_TRIGGER_GLOBAL_THREAD=y CONFIG_LIS3MDL_TRIGGER_GLOBAL_THREAD=y CONFIG_LPS22HH_TRIGGER_GLOBAL_THREAD=y +CONFIG_TSL2540_TRIGGER_GLOBAL_THREAD=y CONFIG_LSM6DSL_TRIGGER_GLOBAL_THREAD=y CONFIG_LSM6DSO_TRIGGER_GLOBAL_THREAD=y CONFIG_LSM6DSO16IS_TRIGGER_GLOBAL_THREAD=y diff --git a/tests/drivers/build_all/sensor/sensors_trigger_none.conf b/tests/drivers/build_all/sensor/sensors_trigger_none.conf index 6fffb6ef623a0f9..1bf506f12b98ffa 100644 --- a/tests/drivers/build_all/sensor/sensors_trigger_none.conf +++ b/tests/drivers/build_all/sensor/sensors_trigger_none.conf @@ -33,6 +33,7 @@ CONFIG_LIS2DW12_TRIGGER_NONE=y CONFIG_LIS2MDL_TRIGGER_NONE=y CONFIG_LIS3MDL_TRIGGER_NONE=y CONFIG_LPS22HH_TRIGGER_NONE=y +CONFIG_TSL2540_TRIGGER_NONE=y CONFIG_LSM6DSL_TRIGGER_NONE=y CONFIG_LSM6DSO_TRIGGER_NONE=y CONFIG_LSM6DSO16IS_TRIGGER_NONE=y diff --git a/tests/drivers/build_all/sensor/sensors_trigger_own.conf b/tests/drivers/build_all/sensor/sensors_trigger_own.conf index f3bb63056727e8d..2879781508ed782 100644 --- a/tests/drivers/build_all/sensor/sensors_trigger_own.conf +++ b/tests/drivers/build_all/sensor/sensors_trigger_own.conf @@ -31,6 +31,7 @@ CONFIG_LIS2DW12_TRIGGER_OWN_THREAD=y CONFIG_LIS2MDL_TRIGGER_OWN_THREAD=y CONFIG_LIS3MDL_TRIGGER_OWN_THREAD=y CONFIG_LPS22HH_TRIGGER_OWN_THREAD=y +CONFIG_TSL2540_TRIGGER_OWN_THREAD=y CONFIG_LSM6DSL_TRIGGER_OWN_THREAD=y CONFIG_LSM6DSO_TRIGGER_OWN_THREAD=y CONFIG_LSM6DSO16IS_TRIGGER_OWN_THREAD=y