From ff3f2e999079c5607b91e71086da1eabf91f7abf Mon Sep 17 00:00:00 2001 From: Rick Talbott Date: Thu, 31 Aug 2023 13:30:12 -0600 Subject: [PATCH] drivers: sensor: tsl2540 Add the tsl2540 sensor to drivers. Signed-off-by: Rick Talbott updated Signed-off-by: Rick Talbott fixed macro 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 | 371 ++++++++++++++++++ drivers/sensor/tsl2540/tsl2540.h | 113 ++++++ drivers/sensor/tsl2540/tsl2540_trigger.c | 198 ++++++++++ dts/bindings/sensor/ams,tsl2540.yaml | 35 ++ include/zephyr/drivers/sensor/tsl2540.h | 52 +++ tests/drivers/build_all/sensor/i2c.dtsi | 6 + .../sensor/sensors_trigger_global.conf | 1 + .../sensor/sensors_trigger_none.conf | 1 + .../build_all/sensor/sensors_trigger_own.conf | 1 + 13 files changed, 840 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 5839ea12e97ae97..f6e9e158a2e684e 100644 --- a/drivers/sensor/CMakeLists.txt +++ b/drivers/sensor/CMakeLists.txt @@ -136,6 +136,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 8311b4b4bf45641..116b40c4bc8f1cb 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -192,6 +192,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..37064c661241d96 --- /dev/null +++ b/drivers/sensor/tsl2540/tsl2540.c @@ -0,0 +1,371 @@ +/* + * Copyright (c) 2022 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 +#include + +#define TSL2540_INTEGRATION_TIME_MS (2.81) +#define TSL2540_DEVICE_FACTOR (53.0) + +#define FIXED_ATTENUATION_TO_DOUBLE(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; + } 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(&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; + } 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) +{ + const struct tsl2540_config *cfg = dev->config; + struct tsl2540_data *data = dev->data; + int ret = 0; + double cpl; + double glass_attenuation = FIXED_ATTENUATION_TO_DOUBLE(cfg->glass_attenuation); + double glass_ir_attenuation = FIXED_ATTENUATION_TO_DOUBLE(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_DOUBLE(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_DOUBLE(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 (!device_is_ready(cfg->i2c_spec.bus)) { + 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), \ + .int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, {0}), \ + 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) diff --git a/drivers/sensor/tsl2540/tsl2540.h b/drivers/sensor/tsl2540/tsl2540.h new file mode 100644 index 000000000000000..96bf7f510e4d445 --- /dev/null +++ b/drivers/sensor/tsl2540/tsl2540.h @@ -0,0 +1,113 @@ +/* + * 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 +#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 (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; + const struct gpio_dt_spec int_gpio; + 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 diff --git a/drivers/sensor/tsl2540/tsl2540_trigger.c b/drivers/sensor/tsl2540/tsl2540_trigger.c new file mode 100644 index 000000000000000..339dbc450dcae6c --- /dev/null +++ b/drivers/sensor/tsl2540/tsl2540_trigger.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2022 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_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 | config->int_gpio.dt_flags); + 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; +} diff --git a/dts/bindings/sensor/ams,tsl2540.yaml b/dts/bindings/sensor/ams,tsl2540.yaml new file mode 100644 index 000000000000000..45dae034656a87d --- /dev/null +++ b/dts/bindings/sensor/ams,tsl2540.yaml @@ -0,0 +1,35 @@ +# 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 + required: false + description: | + Identifies the interrupt pin + + glass-attenuation: + type: int + default: 227205 + description: | + Visible light attenuation. + Integer value for a represenation with 5 decimal points. + Typical value is 2.27205 from datasheet. + Example: 1.2 would be 120000 + + glass-ir-attenuation: + type: int + default: 234305 + description: | + Infa-red light attenuation. + Integer value for a represenation with 5 decimal points. + Typical value is 2.34305 from datasheet. + Example: 1.2 would be 120000 diff --git a/include/zephyr/drivers/sensor/tsl2540.h b/include/zephyr/drivers/sensor/tsl2540.h new file mode 100644 index 000000000000000..63a2adfe48b41f5 --- /dev/null +++ b/include/zephyr/drivers/sensor/tsl2540.h @@ -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 + +#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_ */ diff --git a/tests/drivers/build_all/sensor/i2c.dtsi b/tests/drivers/build_all/sensor/i2c.dtsi index fd957bc5b9b6a93..019db2c0185887f 100644 --- a/tests/drivers/build_all/sensor/i2c.dtsi +++ b/tests/drivers/build_all/sensor/i2c.dtsi @@ -740,3 +740,9 @@ test_i2c_ist8310@6f { reg = <0x6f>; status = "okay"; }; + +test_i2c_tsl2540: tsl2540@74 { + compatible = "ams,tsl2540"; + reg = <0x70>; + int-gpios = <&test_gpio 0 0>; +}; diff --git a/tests/drivers/build_all/sensor/sensors_trigger_global.conf b/tests/drivers/build_all/sensor/sensors_trigger_global.conf index a18d55ab42ea68e..dc8321f1a7dd4a8 100644 --- a/tests/drivers/build_all/sensor/sensors_trigger_global.conf +++ b/tests/drivers/build_all/sensor/sensors_trigger_global.conf @@ -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 diff --git a/tests/drivers/build_all/sensor/sensors_trigger_none.conf b/tests/drivers/build_all/sensor/sensors_trigger_none.conf index b374ff865250025..e53189629ba8691 100644 --- a/tests/drivers/build_all/sensor/sensors_trigger_none.conf +++ b/tests/drivers/build_all/sensor/sensors_trigger_none.conf @@ -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 diff --git a/tests/drivers/build_all/sensor/sensors_trigger_own.conf b/tests/drivers/build_all/sensor/sensors_trigger_own.conf index 04892cf8ad5ec24..4f7e3bbdbfadd82 100644 --- a/tests/drivers/build_all/sensor/sensors_trigger_own.conf +++ b/tests/drivers/build_all/sensor/sensors_trigger_own.conf @@ -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