diff --git a/drivers/sensor/CMakeLists.txt b/drivers/sensor/CMakeLists.txt index afa200a06ab..b470865ee31 100644 --- a/drivers/sensor/CMakeLists.txt +++ b/drivers/sensor/CMakeLists.txt @@ -68,6 +68,7 @@ add_subdirectory_ifdef(CONFIG_ISM330DHCX ism330dhcx) add_subdirectory_ifdef(CONFIG_ITDS wsen_itds) add_subdirectory_ifdef(CONFIG_LIS2DH lis2dh) add_subdirectory_ifdef(CONFIG_LIS2DS12 lis2ds12) +add_subdirectory_ifdef(CONFIG_LIS2DU12 lis2du12) add_subdirectory_ifdef(CONFIG_LIS2DW12 lis2dw12) add_subdirectory_ifdef(CONFIG_LIS2MDL lis2mdl) add_subdirectory_ifdef(CONFIG_LIS3MDL lis3mdl) diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig index 7e454740973..79bc70fada8 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -140,6 +140,7 @@ source "drivers/sensor/ite_tach_it8xxx2/Kconfig" source "drivers/sensor/ite_vcmp_it8xxx2/Kconfig" source "drivers/sensor/lis2dh/Kconfig" source "drivers/sensor/lis2ds12/Kconfig" +source "drivers/sensor/lis2du12/Kconfig" source "drivers/sensor/lis2dw12/Kconfig" source "drivers/sensor/lis2mdl/Kconfig" source "drivers/sensor/lis3mdl/Kconfig" diff --git a/drivers/sensor/lis2du12/CMakeLists.txt b/drivers/sensor/lis2du12/CMakeLists.txt new file mode 100644 index 00000000000..378cc8f7f0d --- /dev/null +++ b/drivers/sensor/lis2du12/CMakeLists.txt @@ -0,0 +1,12 @@ +# ST Microelectronics LIS2DU12 3-axis accelerometer sensor driver +# +# Copyright (c) 2023 STMicroelectronics +# +# SPDX-License-Identifier: Apache-2.0 +# +zephyr_library() + +zephyr_library_sources(lis2du12.c) +zephyr_library_sources_ifdef(CONFIG_LIS2DU12_TRIGGER lis2du12_trigger.c) + +zephyr_library_include_directories(../stmemsc) diff --git a/drivers/sensor/lis2du12/Kconfig b/drivers/sensor/lis2du12/Kconfig new file mode 100644 index 00000000000..24802d5fa6f --- /dev/null +++ b/drivers/sensor/lis2du12/Kconfig @@ -0,0 +1,24 @@ +# ST Microelectronics LIS2DU12 3-axis accelerometer sensor driver + +# Copyright (c) 2023 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +menuconfig LIS2DU12 + bool "LIS2DU12 I2C/SPI smartxl Chip" + default y + depends on DT_HAS_ST_LIS2DU12_ENABLED + select I2C if $(dt_compat_on_bus,$(DT_COMPAT_ST_LIS2DU12),i2c) + select SPI if $(dt_compat_on_bus,$(DT_COMPAT_ST_LIS2DU12),spi) + select HAS_STMEMSC + select USE_STDC_LIS2DU12 + help + Enable driver for LIS2DU12 smartxl sensor. + +if LIS2DU12 + +module = LIS2DU12 +thread_priority = 10 +thread_stack_size = 1024 +source "drivers/sensor/Kconfig.trigger_template" + +endif # LIS2DU12 diff --git a/drivers/sensor/lis2du12/lis2du12.c b/drivers/sensor/lis2du12/lis2du12.c new file mode 100644 index 00000000000..bd5d8b2faf0 --- /dev/null +++ b/drivers/sensor/lis2du12/lis2du12.c @@ -0,0 +1,471 @@ +/* ST Microelectronics LIS2DU12 3-axis accelerometer sensor driver + * + * Copyright (c) 2023 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://www.st.com/resource/en/datasheet/lis2du12.pdf + */ + +#define DT_DRV_COMPAT st_lis2du12 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lis2du12.h" + +LOG_MODULE_REGISTER(LIS2DU12, CONFIG_SENSOR_LOG_LEVEL); + +static const float lis2du12_odr_map[14] = { + 0.0f, 1.6f, 3.0f, 6.0f, 6.0f, 12.5f, 25.0f, + 50.0f, 100.0f, 200.0f, 400.0f, 800.0f, 0.0f, 0.0f}; + +static int lis2du12_freq_to_odr_val(const struct device *dev, uint16_t freq) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(lis2du12_odr_map); i++) { + if (freq <= lis2du12_odr_map[i]) { + return i; + } + } + + return -EINVAL; +} + +static const uint16_t lis2du12_accel_fs_map[] = {2, 4, 8, 16}; + +static int lis2du12_accel_range_to_fs_val(int32_t range) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(lis2du12_accel_fs_map); i++) { + if (range == lis2du12_accel_fs_map[i]) { + return i; + } + } + + return -EINVAL; +} + +static inline int lis2du12_reboot(const struct device *dev) +{ + const struct lis2du12_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; + lis2du12_status_t status; + uint8_t tries = 10; + + if (lis2du12_init_set(ctx, LIS2DU12_RESET) < 0) { + return -EIO; + } + + do { + if (!--tries) { + LOG_ERR("sw reset timed out"); + return -ETIMEDOUT; + } + k_usleep(50); + + if (lis2du12_status_get(ctx, &status) < 0) { + return -EIO; + } + } while (status.sw_reset != 0); + + if (lis2du12_init_set(ctx, LIS2DU12_DRV_RDY) < 0) { + return -EIO; + } + + return 0; +} + +static int lis2du12_accel_set_fs_raw(const struct device *dev, uint8_t fs) +{ + const struct lis2du12_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; + struct lis2du12_data *data = dev->data; + lis2du12_md_t mode; + + if (lis2du12_mode_get(ctx, &mode) < 0) { + return -EIO; + } + + mode.fs = fs; + if (lis2du12_mode_set(ctx, &mode) < 0) { + return -EIO; + } + + data->accel_fs = fs; + + return 0; +} + +static int lis2du12_accel_set_odr_raw(const struct device *dev, uint8_t odr) +{ + const struct lis2du12_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; + struct lis2du12_data *data = dev->data; + lis2du12_md_t mode; + + if (lis2du12_mode_get(ctx, &mode) < 0) { + return -EIO; + } + + mode.odr = odr; + if (lis2du12_mode_set(ctx, &mode) < 0) { + return -EIO; + } + + data->accel_freq = odr; + + return 0; +} + +static int lis2du12_accel_odr_set(const struct device *dev, uint16_t freq) +{ + int odr; + + odr = lis2du12_freq_to_odr_val(dev, freq); + if (odr < 0) { + return odr; + } + + if (lis2du12_accel_set_odr_raw(dev, odr) < 0) { + LOG_ERR("failed to set accelerometer sampling rate"); + return -EIO; + } + + return 0; +} + +static int lis2du12_accel_range_set(const struct device *dev, int32_t range) +{ + int fs; + struct lis2du12_data *data = dev->data; + + fs = lis2du12_accel_range_to_fs_val(range); + if (fs < 0) { + return fs; + } + + if (lis2du12_accel_set_fs_raw(dev, fs) < 0) { + LOG_ERR("failed to set accelerometer full-scale"); + return -EIO; + } + + data->acc_gain = lis2du12_accel_fs_map[fs] * GAIN_UNIT_XL / 2; + return 0; +} + +static int lis2du12_accel_config(const struct device *dev, + enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + switch (attr) { + case SENSOR_ATTR_FULL_SCALE: + return lis2du12_accel_range_set(dev, sensor_ms2_to_g(val)); + case SENSOR_ATTR_SAMPLING_FREQUENCY: + return lis2du12_accel_odr_set(dev, val->val1); + default: + LOG_WRN("Accel attribute %d not supported.", attr); + return -ENOTSUP; + } + + return 0; +} + +static int lis2du12_attr_set(const struct device *dev, + enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + switch (chan) { + case SENSOR_CHAN_ACCEL_XYZ: + return lis2du12_accel_config(dev, chan, attr, val); + default: + LOG_WRN("attribute %d not supported on this channel.", chan); + return -ENOTSUP; + } + + return 0; +} + +static int lis2du12_sample_fetch_accel(const struct device *dev) +{ + const struct lis2du12_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; + struct lis2du12_data *data = dev->data; + lis2du12_data_t xl_data; + lis2du12_md_t md; + + md.fs = cfg->accel_range; + if (lis2du12_data_get(ctx, &md, &xl_data) < 0) { + LOG_ERR("Failed to read sample"); + return -EIO; + } + + data->acc[0] = xl_data.xl.raw[0]; + data->acc[1] = xl_data.xl.raw[1]; + data->acc[2] = xl_data.xl.raw[2]; + + return 0; +} + +static int lis2du12_sample_fetch(const struct device *dev, + enum sensor_channel chan) +{ + switch (chan) { + case SENSOR_CHAN_ACCEL_XYZ: + lis2du12_sample_fetch_accel(dev); + break; + case SENSOR_CHAN_ALL: + lis2du12_sample_fetch_accel(dev); + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static inline void lis2du12_accel_convert(struct sensor_value *val, int raw_val, + uint32_t sensitivity) +{ + int64_t dval; + + /* Sensitivity is exposed in ug/LSB */ + /* Convert to m/s^2 */ + dval = (int64_t)(raw_val) * sensitivity * SENSOR_G_DOUBLE; + val->val1 = (int32_t)(dval / 1000000); + val->val2 = (int32_t)(dval % 1000000); + +} + +static inline int lis2du12_accel_get_channel(enum sensor_channel chan, + struct sensor_value *val, + struct lis2du12_data *data, + uint32_t sensitivity) +{ + uint8_t i; + + switch (chan) { + case SENSOR_CHAN_ACCEL_X: + lis2du12_accel_convert(val, data->acc[0], sensitivity); + break; + case SENSOR_CHAN_ACCEL_Y: + lis2du12_accel_convert(val, data->acc[1], sensitivity); + break; + case SENSOR_CHAN_ACCEL_Z: + lis2du12_accel_convert(val, data->acc[2], sensitivity); + break; + case SENSOR_CHAN_ACCEL_XYZ: + for (i = 0; i < 3; i++) { + lis2du12_accel_convert(val++, data->acc[i], sensitivity); + } + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static int lis2du12_accel_channel_get(enum sensor_channel chan, + struct sensor_value *val, + struct lis2du12_data *data) +{ + return lis2du12_accel_get_channel(chan, val, data, data->acc_gain); +} + +static int lis2du12_channel_get(const struct device *dev, + enum sensor_channel chan, + struct sensor_value *val) +{ + struct lis2du12_data *data = dev->data; + + switch (chan) { + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + case SENSOR_CHAN_ACCEL_XYZ: + lis2du12_accel_channel_get(chan, val, data); + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static const struct sensor_driver_api lis2du12_driver_api = { + .attr_set = lis2du12_attr_set, +#if CONFIG_LIS2DU12_TRIGGER + .trigger_set = lis2du12_trigger_set, +#endif + .sample_fetch = lis2du12_sample_fetch, + .channel_get = lis2du12_channel_get, +}; + +static int lis2du12_init_chip(const struct device *dev) +{ + const struct lis2du12_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; + struct lis2du12_data *lis2du12 = dev->data; + lis2du12_id_t chip_id; + uint8_t odr, fs; + + if (lis2du12_id_get(ctx, &chip_id) < 0) { + LOG_ERR("Failed reading chip id"); + return -EIO; + } + + LOG_INF("chip id 0x%x", chip_id.whoami); + + if (chip_id.whoami != LIS2DU12_ID) { + LOG_ERR("Invalid chip id 0x%x", chip_id.whoami); + return -EIO; + } + + /* reboot device */ + if (lis2du12_reboot(dev) < 0) { + return -EIO; + } + + /* set FS from DT */ + fs = cfg->accel_range; + LOG_DBG("accel range is %d", fs); + if (lis2du12_accel_set_fs_raw(dev, fs) < 0) { + LOG_ERR("failed to set accelerometer range %d", fs); + return -EIO; + } + lis2du12->acc_gain = lis2du12_accel_fs_map[fs] * GAIN_UNIT_XL / 2; + + /* set odr from DT (the only way to go in high performance) */ + odr = cfg->accel_odr; + LOG_DBG("accel odr is %d", odr); + if (lis2du12_accel_set_odr_raw(dev, odr) < 0) { + LOG_ERR("failed to set accelerometer odr %d", odr); + return -EIO; + } + + return 0; +} + +static int lis2du12_init(const struct device *dev) +{ +#ifdef CONFIG_LIS2DU12_TRIGGER + const struct lis2du12_config *cfg = dev->config; +#endif + struct lis2du12_data *data = dev->data; + + LOG_INF("Initialize device %s", dev->name); + data->dev = dev; + + if (lis2du12_init_chip(dev) < 0) { + LOG_ERR("failed to initialize chip"); + return -EIO; + } + +#ifdef CONFIG_LIS2DU12_TRIGGER + if (cfg->trig_enabled) { + if (lis2du12_init_interrupt(dev) < 0) { + LOG_ERR("Failed to initialize interrupt."); + return -EIO; + } + } +#endif + + return 0; +} + +/* + * Device creation macro, shared by LIS2DU12_DEFINE_SPI() and + * LIS2DU12_DEFINE_I2C(). + */ + +#define LIS2DU12_DEVICE_INIT(inst) \ + SENSOR_DEVICE_DT_INST_DEFINE(inst, \ + lis2du12_init, \ + NULL, \ + &lis2du12_data_##inst, \ + &lis2du12_config_##inst, \ + POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, \ + &lis2du12_driver_api); + +/* + * Instantiation macros used when a device is on a SPI bus. + */ + +#ifdef CONFIG_LIS2DU12_TRIGGER +#define LIS2DU12_CFG_IRQ(inst) \ + .trig_enabled = true, \ + .int1_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int1_gpios, { 0 }), \ + .int2_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int2_gpios, { 0 }), \ + .drdy_pulsed = DT_INST_PROP(inst, drdy_pulsed), \ + .drdy_pin = DT_INST_PROP(inst, drdy_pin) +#else +#define LIS2DU12_CFG_IRQ(inst) +#endif /* CONFIG_LIS2DU12_TRIGGER */ + +#define LIS2DU12_SPI_OP (SPI_WORD_SET(8) | \ + SPI_OP_MODE_MASTER | \ + SPI_MODE_CPOL | \ + SPI_MODE_CPHA) \ + +#define LIS2DU12_CONFIG_COMMON(inst) \ + .accel_odr = DT_INST_PROP(inst, accel_odr), \ + .accel_range = DT_INST_PROP(inst, accel_range), \ + IF_ENABLED(UTIL_OR(DT_INST_NODE_HAS_PROP(inst, int1_gpios), \ + DT_INST_NODE_HAS_PROP(inst, int2_gpios)), \ + (LIS2DU12_CFG_IRQ(inst))) + +/* + * Instantiation macros used when a device is on a SPI bus. + */ + +#define LIS2DU12_CONFIG_SPI(inst) \ + { \ + STMEMSC_CTX_SPI(&lis2du12_config_##inst.stmemsc_cfg), \ + .stmemsc_cfg = { \ + .spi = SPI_DT_SPEC_INST_GET(inst, \ + LIS2DU12_SPI_OP, \ + 0), \ + }, \ + LIS2DU12_CONFIG_COMMON(inst) \ + } + +/* + * Instantiation macros used when a device is on an I2C bus. + */ + +#define LIS2DU12_CONFIG_I2C(inst) \ + { \ + STMEMSC_CTX_I2C(&lis2du12_config_##inst.stmemsc_cfg), \ + .stmemsc_cfg = { \ + .i2c = I2C_DT_SPEC_INST_GET(inst), \ + }, \ + LIS2DU12_CONFIG_COMMON(inst) \ + } + +/* + * Main instantiation macro. Use of COND_CODE_1() selects the right + * bus-specific macro at preprocessor time. + */ + +#define LIS2DU12_DEFINE(inst) \ + static struct lis2du12_data lis2du12_data_##inst; \ + static const struct lis2du12_config lis2du12_config_##inst = \ + COND_CODE_1(DT_INST_ON_BUS(inst, spi), \ + (LIS2DU12_CONFIG_SPI(inst)), \ + (LIS2DU12_CONFIG_I2C(inst))); \ + LIS2DU12_DEVICE_INIT(inst) + +DT_INST_FOREACH_STATUS_OKAY(LIS2DU12_DEFINE) diff --git a/drivers/sensor/lis2du12/lis2du12.h b/drivers/sensor/lis2du12/lis2du12.h new file mode 100644 index 00000000000..f05d0511b7b --- /dev/null +++ b/drivers/sensor/lis2du12/lis2du12.h @@ -0,0 +1,98 @@ +/* ST Microelectronics LIS2DU12 3-axis accelerometer sensor driver + * + * Copyright (c) 2023 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://www.st.com/resource/en/datasheet/lis2du12.pdf + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_LIS2DU12_LIS2DU12_H_ +#define ZEPHYR_DRIVERS_SENSOR_LIS2DU12_LIS2DU12_H_ + +#include +#include +#include +#include +#include +#include "lis2du12_reg.h" + +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) +#include +#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) */ + +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) +#include +#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */ + +#define LIS2DU12_EN_BIT 0x01 +#define LIS2DU12_DIS_BIT 0x00 + +/* Accel sensor sensitivity grain is 61 ug/LSB */ +#define GAIN_UNIT_XL (61LL) + +#define SENSOR_G_DOUBLE (SENSOR_G / 1000000.0) + +struct lis2du12_config { + stmdev_ctx_t ctx; + union { +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) + const struct i2c_dt_spec i2c; +#endif +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) + const struct spi_dt_spec spi; +#endif + } stmemsc_cfg; + uint8_t accel_pm; + uint8_t accel_odr; + uint8_t accel_range; + uint8_t drdy_pulsed; +#ifdef CONFIG_LIS2DU12_TRIGGER + const struct gpio_dt_spec int1_gpio; + const struct gpio_dt_spec int2_gpio; + uint8_t drdy_pin; + bool trig_enabled; +#endif /* CONFIG_LIS2DU12_TRIGGER */ +}; + +union samples { + uint8_t raw[6]; + struct { + int16_t axis[3]; + }; +} __aligned(2); + +struct lis2du12_data { + const struct device *dev; + int16_t acc[3]; + uint32_t acc_gain; + uint16_t accel_freq; + uint8_t accel_fs; + +#ifdef CONFIG_LIS2DU12_TRIGGER + struct gpio_dt_spec *drdy_gpio; + + struct gpio_callback gpio_cb; + sensor_trigger_handler_t handler_drdy_acc; + const struct sensor_trigger *trig_drdy_acc; + +#if defined(CONFIG_LIS2DU12_TRIGGER_OWN_THREAD) + K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_LIS2DU12_THREAD_STACK_SIZE); + struct k_thread thread; + struct k_sem gpio_sem; +#elif defined(CONFIG_LIS2DU12_TRIGGER_GLOBAL_THREAD) + struct k_work work; +#endif +#endif /* CONFIG_LIS2DU12_TRIGGER */ +}; + +#ifdef CONFIG_LIS2DU12_TRIGGER +int lis2du12_trigger_set(const struct device *dev, + const struct sensor_trigger *trig, + sensor_trigger_handler_t handler); + +int lis2du12_init_interrupt(const struct device *dev); +#endif + +#endif /* ZEPHYR_DRIVERS_SENSOR_LIS2DU12_LIS2DU12_H_ */ diff --git a/drivers/sensor/lis2du12/lis2du12_trigger.c b/drivers/sensor/lis2du12/lis2du12_trigger.c new file mode 100644 index 00000000000..d2edbad3e2b --- /dev/null +++ b/drivers/sensor/lis2du12/lis2du12_trigger.c @@ -0,0 +1,215 @@ +/* ST Microelectronics LIS2DU12 3-axis accelerometer sensor driver + * + * Copyright (c) 2023 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://www.st.com/resource/en/datasheet/lis2du12.pdf + */ + +#define DT_DRV_COMPAT st_lis2du12 + +#include +#include +#include +#include + +#include "lis2du12.h" + +LOG_MODULE_DECLARE(LIS2DU12, CONFIG_SENSOR_LOG_LEVEL); + +/** + * lis2du12_enable_xl_int - XL enable selected int pin to generate interrupt + */ +static int lis2du12_enable_xl_int(const struct device *dev, int enable) +{ + const struct lis2du12_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; + int ret; + + if (enable) { + lis2du12_md_t md; + lis2du12_data_t xl_data; + + /* dummy read: re-trigger interrupt */ + md.fs = cfg->accel_range; + lis2du12_data_get(ctx, &md, &xl_data); + } + + /* set interrupt */ + if (cfg->drdy_pin == 1) { + lis2du12_pin_int_route_t val; + + ret = lis2du12_pin_int1_route_get(ctx, &val); + if (ret < 0) { + LOG_ERR("pint_int1_route_get error"); + return ret; + } + + val.drdy_xl = 1; + + ret = lis2du12_pin_int1_route_set(ctx, &val); + } else { + lis2du12_pin_int_route_t val; + + ret = lis2du12_pin_int2_route_get(ctx, &val); + if (ret < 0) { + LOG_ERR("pint_int2_route_get error"); + return ret; + } + + val.drdy_xl = 1; + + ret = lis2du12_pin_int2_route_set(ctx, &val); + } + + return ret; +} + +/** + * lis2du12_trigger_set - link external trigger to event data ready + */ +int lis2du12_trigger_set(const struct device *dev, + const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) +{ + const struct lis2du12_config *cfg = dev->config; + struct lis2du12_data *lis2du12 = dev->data; + + if (!cfg->trig_enabled) { + LOG_ERR("trigger_set op not supported"); + return -ENOTSUP; + } + + switch (trig->chan) { + case SENSOR_CHAN_ACCEL_XYZ: + lis2du12->handler_drdy_acc = handler; + lis2du12->trig_drdy_acc = trig; + if (handler) { + return lis2du12_enable_xl_int(dev, LIS2DU12_EN_BIT); + } + + return lis2du12_enable_xl_int(dev, LIS2DU12_DIS_BIT); + + default: + return -ENOTSUP; + } + +} + +/** + * lis2du12_handle_interrupt - handle the drdy event + * read data and call handler if registered any + */ +static void lis2du12_handle_interrupt(const struct device *dev) +{ + struct lis2du12_data *lis2du12 = dev->data; + const struct lis2du12_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; + lis2du12_status_t status; + + while (1) { + if (lis2du12_status_get(ctx, &status) < 0) { + LOG_ERR("failed reading status reg"); + return; + } + + if (status.drdy_xl == 0) { + break; + } + + if ((status.drdy_xl) && (lis2du12->handler_drdy_acc != NULL)) { + lis2du12->handler_drdy_acc(dev, lis2du12->trig_drdy_acc); + } + } + + gpio_pin_interrupt_configure_dt(lis2du12->drdy_gpio, + GPIO_INT_EDGE_TO_ACTIVE); +} + +static void lis2du12_gpio_callback(const struct device *dev, + struct gpio_callback *cb, uint32_t pins) +{ + struct lis2du12_data *lis2du12 = + CONTAINER_OF(cb, struct lis2du12_data, gpio_cb); + + ARG_UNUSED(pins); + + gpio_pin_interrupt_configure_dt(lis2du12->drdy_gpio, GPIO_INT_DISABLE); + +#if defined(CONFIG_LIS2DU12_TRIGGER_OWN_THREAD) + k_sem_give(&lis2du12->gpio_sem); +#elif defined(CONFIG_LIS2DU12_TRIGGER_GLOBAL_THREAD) + k_work_submit(&lis2du12->work); +#endif /* CONFIG_LIS2DU12_TRIGGER_OWN_THREAD */ +} + +#ifdef CONFIG_LIS2DU12_TRIGGER_OWN_THREAD +static void lis2du12_thread(struct lis2du12_data *lis2du12) +{ + while (1) { + k_sem_take(&lis2du12->gpio_sem, K_FOREVER); + lis2du12_handle_interrupt(lis2du12->dev); + } +} +#endif /* CONFIG_LIS2DU12_TRIGGER_OWN_THREAD */ + +#ifdef CONFIG_LIS2DU12_TRIGGER_GLOBAL_THREAD +static void lis2du12_work_cb(struct k_work *work) +{ + struct lis2du12_data *lis2du12 = + CONTAINER_OF(work, struct lis2du12_data, work); + + lis2du12_handle_interrupt(lis2du12->dev); +} +#endif /* CONFIG_LIS2DU12_TRIGGER_GLOBAL_THREAD */ + +int lis2du12_init_interrupt(const struct device *dev) +{ + struct lis2du12_data *lis2du12 = dev->data; + const struct lis2du12_config *cfg = dev->config; + int ret; + + lis2du12->drdy_gpio = (cfg->drdy_pin == 1) ? + (struct gpio_dt_spec *)&cfg->int1_gpio : + (struct gpio_dt_spec *)&cfg->int2_gpio; + + /* setup data ready gpio interrupt (INT1 or INT2) */ + if (!gpio_is_ready_dt(lis2du12->drdy_gpio)) { + LOG_ERR("Cannot get pointer to drdy_gpio device (%p)", + lis2du12->drdy_gpio); + return -EINVAL; + } + +#if defined(CONFIG_LIS2DU12_TRIGGER_OWN_THREAD) + k_sem_init(&lis2du12->gpio_sem, 0, K_SEM_MAX_LIMIT); + + k_thread_create(&lis2du12->thread, lis2du12->thread_stack, + CONFIG_LIS2DU12_THREAD_STACK_SIZE, + (k_thread_entry_t)lis2du12_thread, lis2du12, + NULL, NULL, K_PRIO_COOP(CONFIG_LIS2DU12_THREAD_PRIORITY), + 0, K_NO_WAIT); + k_thread_name_set(&lis2du12->thread, dev->name); +#elif defined(CONFIG_LIS2DU12_TRIGGER_GLOBAL_THREAD) + lis2du12->work.handler = lis2du12_work_cb; +#endif /* CONFIG_LIS2DU12_TRIGGER_OWN_THREAD */ + + ret = gpio_pin_configure_dt(lis2du12->drdy_gpio, GPIO_INPUT); + if (ret < 0) { + LOG_ERR("Could not configure gpio: %d", ret); + return ret; + } + + gpio_init_callback(&lis2du12->gpio_cb, + lis2du12_gpio_callback, + BIT(lis2du12->drdy_gpio->pin)); + + if (gpio_add_callback(lis2du12->drdy_gpio->port, &lis2du12->gpio_cb) < 0) { + LOG_ERR("Could not set gpio callback"); + return -EIO; + } + + return gpio_pin_interrupt_configure_dt(lis2du12->drdy_gpio, + GPIO_INT_EDGE_TO_ACTIVE); +} diff --git a/dts/bindings/sensor/st,lis2du12-common.yaml b/dts/bindings/sensor/st,lis2du12-common.yaml new file mode 100644 index 00000000000..d9ca38d9e32 --- /dev/null +++ b/dts/bindings/sensor/st,lis2du12-common.yaml @@ -0,0 +1,97 @@ +# Copyright (c) 2023 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +description: | + When setting the accel-range, accel-odr, properties in a .dts or .dtsi + file you may include lis2du12.h and use the macros defined there. + + Example: + #include + + lis2du12: lis2du12@0 { + ... + + accel-range = ; + accel-odr = ; + }; + +include: sensor-device.yaml + +properties: + int1-gpios: + type: phandle-array + description: | + INT1 pin + + This pin defaults to active high when produced by the sensor. + The property value should ensure the flags properly describe + the signal that is presented to the driver. + + int2-gpios: + type: phandle-array + description: | + INT2 pin + + This pin defaults to active high when produced by the sensor. + The property value should ensure the flags properly describe + the signal that is presented to the driver. + + drdy-pin: + type: int + default: 1 + description: | + Select DRDY pin number (1 or 2). + + 1 = drdy is generated from INT1 + 2 = drdy is generated from INT2 + + This number represents which of the two interrupt pins + (INT1 or INT2) the drdy line is attached to. This property is not + mandatory and if not present it defaults to 1 which is the + configuration at power-up. + enum: [1, 2] + + accel-range: + type: int + default: 0 + description: | + Range in g. Default is power-up configuration. + + 0 # LIS2DU12_DT_FS_2G (0.061 mg/LSB) + 1 # LIS2DU12_DT_FS_4G (0.122 mg/LSB) + 2 # LIS2DU12_DT_FS_8G (0.244 mg/LSB) + 3 # LIS2DU12_DT_FS_16G (0.488 mg/LSB) + + enum: [0, 1, 2, 3] + + accel-odr: + type: int + default: 0x0 + description: | + Specify the default accelerometer output data rate expressed in samples per second (Hz). + The values are taken in accordance to lis2du12_md_t enumerative in hal/st + module. Please note that this values will also enable/disable High performance mode. + Default is power-up configuration. + + 0x00 # LIS2DU12_DT_ODR_OFF + 0x01 # 1Hz6 (ultra low power) + 0x02 # 3Hz (ultra low power) + 0x03 # 25Hz (ultra low power) + 0x04 # 6Hz (low power) + 0x05 # 12Hz5 (low power) + 0x06 # 25Hz (low power) + 0x07 # 50Hz (low power) + 0x08 # 100Hz (low power) + 0x09 # 200Hz (low power) + 0x0a # 400Hz (low power) + 0x0b # 800Hz (low power) + 0x0e # Single-shot high latency by INT2 + 0x0f # Single-shot high latency by IF + + enum: [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0e, 0x0f] + + drdy-pulsed: + type: boolean + description: | + Selects the pulsed mode for data-ready interrupt when enabled, + and the latched mode when disabled. diff --git a/dts/bindings/sensor/st,lis2du12-i2c.yaml b/dts/bindings/sensor/st,lis2du12-i2c.yaml new file mode 100644 index 00000000000..ad09f2601c3 --- /dev/null +++ b/dts/bindings/sensor/st,lis2du12-i2c.yaml @@ -0,0 +1,10 @@ +# Copyright (c) 2023 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +description: | + STMicroelectronics LIS2DU12 3-axis ultra-low power accelerometer sensor + accessed through I2C bus + +compatible: "st,lis2du12" + +include: ["i2c-device.yaml", "st,lis2du12-common.yaml"] diff --git a/dts/bindings/sensor/st,lis2du12-spi.yaml b/dts/bindings/sensor/st,lis2du12-spi.yaml new file mode 100644 index 00000000000..0fad5696827 --- /dev/null +++ b/dts/bindings/sensor/st,lis2du12-spi.yaml @@ -0,0 +1,10 @@ +# Copyright (c) 2023 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +description: | + STMicroelectronics LIS2DU12 3-axis ultra-low power accelerometer sensor + accessed through SPI bus + +compatible: "st,lis2du12" + +include: ["spi-device.yaml", "st,lis2du12-common.yaml"] diff --git a/include/zephyr/dt-bindings/sensor/lis2du12.h b/include/zephyr/dt-bindings/sensor/lis2du12.h new file mode 100644 index 00000000000..99e4c520e3f --- /dev/null +++ b/include/zephyr/dt-bindings/sensor/lis2du12.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_ST_LIS2DU12_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_ST_LIS2DU12_H_ + +/* Accel range */ +#define LIS2DU12_DT_FS_2G 0 +#define LIS2DU12_DT_FS_4G 1 +#define LIS2DU12_DT_FS_8G 2 +#define LIS2DU12_DT_FS_16G 3 + +/* Accel rates */ +#define LIS2DU12_DT_ODR_OFF 0x00 /* Power-Down */ +#define LIS2DU12_DT_ODR_AT_1Hz6_ULP 0x01 /* 1Hz6 (ultra low power) */ +#define LIS2DU12_DT_ODR_AT_3Hz_ULP 0x02 /* 3Hz (ultra low power) */ +#define LIS2DU12_DT_ODR_AT_6Hz_ULP 0x03 /* 6Hz (ultra low power) */ +#define LIS2DU12_DT_ODR_AT_6Hz 0x04 /* 6Hz (normal) */ +#define LIS2DU12_DT_ODR_AT_12Hz 0x05 /* 12Hz5 (normal) */ +#define LIS2DU12_DT_ODR_AT_25Hz 0x06 /* 25Hz (normal) */ +#define LIS2DU12_DT_ODR_AT_50Hz 0x07 /* 50Hz (normal) */ +#define LIS2DU12_DT_ODR_AT_100Hz 0x08 /* 100Hz (normal) */ +#define LIS2DU12_DT_ODR_AT_200Hz 0x09 /* 200Hz (normal) */ +#define LIS2DU12_DT_ODR_AT_400Hz 0x0a /* 400Hz (normal) */ +#define LIS2DU12_DT_ODR_AT_800Hz 0x0b /* 800Hz (normal) */ +#define LIS2DU12_DT_ODR_TRIG_PIN 0x0e /* Single-shot high latency by INT2 */ +#define LIS2DU12_DT_ODR_TRIG_SW 0x0f /* Single-shot high latency by IF */ + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_ST_LIS2DU12_H_ */ diff --git a/modules/Kconfig.st b/modules/Kconfig.st index 371f1063349..d853b742512 100644 --- a/modules/Kconfig.st +++ b/modules/Kconfig.st @@ -88,6 +88,9 @@ config USE_STDC_LIS2DS12 config USE_STDC_LIS2DTW12 bool +config USE_STDC_LIS2DU12 + bool + config USE_STDC_LIS2DW12 bool diff --git a/tests/drivers/build_all/sensor/app.overlay b/tests/drivers/build_all/sensor/app.overlay index 9f7c604bf30..c836a2b5fa8 100644 --- a/tests/drivers/build_all/sensor/app.overlay +++ b/tests/drivers/build_all/sensor/app.overlay @@ -122,7 +122,8 @@ <&test_gpio 0 0>, <&test_gpio 0 0>, /* 0x25 */ <&test_gpio 0 0>, /* 0x26 */ - <&test_gpio 0 0>; /* 0x27 */ + <&test_gpio 0 0>, /* 0x27 */ + <&test_gpio 0 0>; /* 0x28 */ #include "spi.dtsi" }; diff --git a/tests/drivers/build_all/sensor/i2c.dtsi b/tests/drivers/build_all/sensor/i2c.dtsi index 23abb1a47a4..9387a24cc85 100644 --- a/tests/drivers/build_all/sensor/i2c.dtsi +++ b/tests/drivers/build_all/sensor/i2c.dtsi @@ -17,6 +17,7 @@ #include #include #include +#include /**************************************** * PLEASE KEEP REG ADDRESSES SEQUENTIAL * @@ -869,3 +870,12 @@ test_i2c_lps28dfw: lps28dfw@7b { avg = ; fs = ; }; + +test_i2c_lis2du12: lis2du12@7c { + compatible = "st,lis2du12"; + reg = <0x7c>; + int1-gpios = <&test_gpio 0 0>; + int2-gpios = <&test_gpio 0 0>; + accel-range = ; + accel-odr = ; +}; diff --git a/tests/drivers/build_all/sensor/sensors_trigger_global.conf b/tests/drivers/build_all/sensor/sensors_trigger_global.conf index 197f61bd74d..2dc30736420 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_ISL29035_TRIGGER_GLOBAL_THREAD=y CONFIG_ISM330DHCX_TRIGGER_GLOBAL_THREAD=y CONFIG_LIS2DH_TRIGGER_GLOBAL_THREAD=y CONFIG_LIS2DS12_TRIGGER_GLOBAL_THREAD=y +CONFIG_LIS2DU12_TRIGGER_GLOBAL_THREAD=y CONFIG_LIS2DW12_TRIGGER_GLOBAL_THREAD=y CONFIG_LIS2MDL_TRIGGER_GLOBAL_THREAD=y CONFIG_LIS3MDL_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 cd9dfac3915..3012c09845b 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_ISL29035_TRIGGER_NONE=y CONFIG_ISM330DHCX_TRIGGER_NONE=y CONFIG_LIS2DH_TRIGGER_NONE=y CONFIG_LIS2DS12_TRIGGER_NONE=y +CONFIG_LIS2DU12_TRIGGER_NONE=y CONFIG_LIS2DW12_TRIGGER_NONE=y CONFIG_LIS2MDL_TRIGGER_NONE=y CONFIG_LIS3MDL_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 05ed9500fe3..79ee1f82eae 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_ISL29035_TRIGGER_OWN_THREAD=y CONFIG_ISM330DHCX_TRIGGER_OWN_THREAD=y CONFIG_LIS2DH_TRIGGER_OWN_THREAD=y CONFIG_LIS2DS12_TRIGGER_OWN_THREAD=y +CONFIG_LIS2DU12_TRIGGER_OWN_THREAD=y CONFIG_LIS2DW12_TRIGGER_OWN_THREAD=y CONFIG_LIS2MDL_TRIGGER_OWN_THREAD=y CONFIG_LIS3MDL_TRIGGER_OWN_THREAD=y diff --git a/tests/drivers/build_all/sensor/spi.dtsi b/tests/drivers/build_all/sensor/spi.dtsi index cd7bf1b66ec..120b990fccd 100644 --- a/tests/drivers/build_all/sensor/spi.dtsi +++ b/tests/drivers/build_all/sensor/spi.dtsi @@ -312,3 +312,12 @@ test_spi_lps22df: lps22df@27 { drdy-gpios = <&test_gpio 0 0>; status = "okay"; }; + +test_spi_lis2du12: lis2du12@28 { + compatible = "st,lis2du12"; + reg = <0x28>; + spi-max-frequency = <0>; + int1-gpios = <&test_gpio 0 0>; + int2-gpios = <&test_gpio 0 0>; + status = "okay"; +};