diff --git a/drivers/sensor/st/CMakeLists.txt b/drivers/sensor/st/CMakeLists.txt index 816a4cf0d4f4..10819512e6c9 100644 --- a/drivers/sensor/st/CMakeLists.txt +++ b/drivers/sensor/st/CMakeLists.txt @@ -11,6 +11,7 @@ add_subdirectory_ifdef(CONFIG_IIS2ICLX iis2iclx) add_subdirectory_ifdef(CONFIG_IIS2MDC iis2mdc) add_subdirectory_ifdef(CONFIG_IIS328DQ iis328dq) add_subdirectory_ifdef(CONFIG_IIS3DHHC iis3dhhc) +add_subdirectory_ifdef(CONFIG_IIS3DWB iis3dwb) add_subdirectory_ifdef(CONFIG_ISM330DHCX ism330dhcx) add_subdirectory_ifdef(CONFIG_LIS2DE12 lis2de12) add_subdirectory_ifdef(CONFIG_LIS2DH lis2dh) diff --git a/drivers/sensor/st/Kconfig b/drivers/sensor/st/Kconfig index a38b12109419..518411906f41 100644 --- a/drivers/sensor/st/Kconfig +++ b/drivers/sensor/st/Kconfig @@ -10,6 +10,7 @@ source "drivers/sensor/st/iis2iclx/Kconfig" source "drivers/sensor/st/iis2mdc/Kconfig" source "drivers/sensor/st/iis328dq/Kconfig" source "drivers/sensor/st/iis3dhhc/Kconfig" +source "drivers/sensor/st/iis3dwb/Kconfig" source "drivers/sensor/st/ism330dhcx/Kconfig" source "drivers/sensor/st/lis2de12/Kconfig" source "drivers/sensor/st/lis2dh/Kconfig" diff --git a/drivers/sensor/st/iis3dwb/CMakeLists.txt b/drivers/sensor/st/iis3dwb/CMakeLists.txt new file mode 100644 index 000000000000..160b69304c46 --- /dev/null +++ b/drivers/sensor/st/iis3dwb/CMakeLists.txt @@ -0,0 +1,14 @@ +# ST Microelectronics IIS3DWB accelerometer sensor +# +# Copyright (c) 2025 STMicroelectronics +# +# SPDX-License-Identifier: Apache-2.0 +# +zephyr_library() + +zephyr_library_sources(iis3dwb.c) +zephyr_library_sources_ifdef(CONFIG_IIS3DWB_TRIGGER iis3dwb_trigger.c) +zephyr_library_sources_ifdef(CONFIG_SENSOR_ASYNC_API iis3dwb_decoder.c) +zephyr_library_sources_ifdef(CONFIG_IIS3DWB_STREAM iis3dwb_stream.c) + +zephyr_library_include_directories(../stmemsc) diff --git a/drivers/sensor/st/iis3dwb/Kconfig b/drivers/sensor/st/iis3dwb/Kconfig new file mode 100644 index 000000000000..33bd9b9a58e2 --- /dev/null +++ b/drivers/sensor/st/iis3dwb/Kconfig @@ -0,0 +1,36 @@ +# ST Microelectronics IIS3DWB accelerometer sensor + +# Copyright (c) 2025 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +menuconfig IIS3DWB + bool "IIS3DWB accelerometer sensor" + default y + depends on DT_HAS_ST_IIS3DWB_ENABLED + depends on ZEPHYR_HAL_ST_MODULE + select SPI + select HAS_STMEMSC + select USE_STDC_IIS3DWB + help + Enable driver for IIS3DWB SPI-based accelerometer sensor. + +if IIS3DWB + +config IIS3DWB_STREAM + bool "IIS3DWB data streaming" + select IIS3DWB_TRIGGER + default y + depends on SPI_RTIO + depends on SENSOR_ASYNC_API + help + Use this config option to enable streaming sensor data via RTIO subsystem. + +config IIS3DWB_TRIGGER + bool + +config IIS3DWB_ENABLE_TEMP + bool "Temperature" + help + Enable/disable temperature sensor + +endif # IIS3DWB diff --git a/drivers/sensor/st/iis3dwb/iis3dwb.c b/drivers/sensor/st/iis3dwb/iis3dwb.c new file mode 100644 index 000000000000..d4295c7dcd03 --- /dev/null +++ b/drivers/sensor/st/iis3dwb/iis3dwb.c @@ -0,0 +1,455 @@ +/* ST Microelectronics IIS3DWB accelerometer senor + * + * Copyright (c) 2025 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://www.st.com/resource/en/datasheet/iis3dwb.pdf + */ + +#define DT_DRV_COMPAT st_iis3dwb + +#include +#include +#include +#include +#include + +#include "iis3dwb.h" +#include "iis3dwb_rtio.h" + +LOG_MODULE_REGISTER(IIS3DWB, CONFIG_SENSOR_LOG_LEVEL); + +static int32_t iis3dwb_set_range_raw(const struct device *dev, uint8_t range) +{ + struct iis3dwb_data *data = dev->data; + const struct iis3dwb_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; + + data->range = range; + return iis3dwb_xl_full_scale_set(ctx, range); +} + +static int32_t iis3dwb_set_odr_raw(const struct device *dev, uint8_t odr) +{ + struct iis3dwb_data *data = dev->data; + const struct iis3dwb_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; + + data->odr = odr; + return iis3dwb_xl_data_rate_set(ctx, odr); +} + +static int iis3dwb_odr_set(const struct device *dev, + const struct sensor_value *val) +{ + iis3dwb_odr_xl_t odr; + + switch (val->val1) { + case 0: + odr = IIS3DWB_XL_ODR_OFF; + break; + + default: + if (val->val1 > 26) { + LOG_ERR("%s: odr %d Hz not supported", dev->name, val->val1); + return -EINVAL; + } + + odr = IIS3DWB_XL_ODR_26k7Hz; + break; + } + + if (iis3dwb_set_odr_raw(dev, odr)) { + LOG_DBG("failed to set sampling rate"); + return -EIO; + } + + return 0; +} + +static int iis3dwb_set_fs(const struct device *dev, int32_t fs) +{ + int ret; + uint8_t range; + + if (fs <= 2) { + range = IIS3DWB_DT_FS_2G; + } else if (fs <= 4) { + range = IIS3DWB_DT_FS_4G; + } else if (fs <= 8) { + range = IIS3DWB_DT_FS_8G; + } else if (fs <= 16) { + range = IIS3DWB_DT_FS_16G; + } else { + LOG_ERR("fs [%d] not supported.", fs); + return -EINVAL; + } + + ret = iis3dwb_set_range_raw(dev, range); + if (ret < 0) { + LOG_ERR("%s: range init error %d", dev->name, range); + return ret; + } + + LOG_DBG("%s: set fs to %d g", dev->name, fs); + return ret; +} + +static int iis3dwb_attr_set(const struct device *dev, + enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + if (chan != SENSOR_CHAN_ALL) { + LOG_WRN("attr_set() not supported on this channel."); + return -ENOTSUP; + } + + switch (attr) { + case SENSOR_ATTR_FULL_SCALE: + return iis3dwb_set_fs(dev, sensor_ms2_to_g(val)); + + case SENSOR_ATTR_SAMPLING_FREQUENCY: + return iis3dwb_odr_set(dev, val); + default: + LOG_DBG("operation not supported."); + return -ENOTSUP; + } + + return 0; +} + +static void iis3dwb_one_shot_complete_cb(struct rtio *ctx, + const struct rtio_sqe *sqe, + void *arg) +{ + struct rtio_iodev_sqe *iodev_sqe = (struct rtio_iodev_sqe *)sqe->userdata; + struct rtio_cqe *cqe; + int err = 0; + + do { + cqe = rtio_cqe_consume(ctx); + if (cqe != NULL) { + err = cqe->result; + rtio_cqe_release(ctx, cqe); + } + } while (cqe != NULL); + + if (err) { + rtio_iodev_sqe_err(iodev_sqe, err); + } else { + rtio_iodev_sqe_ok(iodev_sqe, 0); + } +} + +/* + * Create a chain of SQEs representing a bus transaction to read a reg. + * The RTIO-enabled bus driver will: + * + * - write "reg" address + * - read "len" data bytes into "buf". + * - call complete_op callback + */ +void iis3dwb_rtio_rd_transaction(const struct device *dev, + uint8_t *regs, uint8_t regs_num, + struct spi_buf *buf, + struct rtio_iodev_sqe *iodev_sqe, + rtio_callback_t complete_op_cb) +{ + struct iis3dwb_data *iis3dwb = dev->data; + struct rtio *rtio = iis3dwb->rtio_ctx; + struct rtio_iodev *iodev = iis3dwb->iodev; + struct rtio_sqe *write_addr; + struct rtio_sqe *read_reg; + struct rtio_sqe *complete_op; + uint8_t i; + + for (i = 0; i < regs_num; i++) { + + write_addr = rtio_sqe_acquire(rtio); + read_reg = rtio_sqe_acquire(rtio); + + if (write_addr == NULL || read_reg == NULL) { + rtio_iodev_sqe_err(iodev_sqe, -ENOMEM); + rtio_sqe_drop_all(rtio); + return; + } + + regs[i] |= 0x80; /* mark the SPI read transaction */ + + rtio_sqe_prep_tiny_write(write_addr, iodev, RTIO_PRIO_NORM, ®s[i], 1, NULL); + write_addr->flags = RTIO_SQE_TRANSACTION; + rtio_sqe_prep_read(read_reg, iodev, RTIO_PRIO_NORM, buf[i].buf, buf[i].len, NULL); + read_reg->flags = RTIO_SQE_CHAINED; + } + + complete_op = rtio_sqe_acquire(rtio); + if (complete_op == NULL) { + rtio_iodev_sqe_err(iodev_sqe, -ENOMEM); + rtio_sqe_drop_all(rtio); + return; + } + + rtio_sqe_prep_callback_no_cqe(complete_op, complete_op_cb, (void *)dev, iodev_sqe); + + rtio_submit(rtio, 0); +} + +static void iis3dwb_submit_one_shot(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) +{ + const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data; + const struct sensor_chan_spec *const channels = cfg->channels; + const size_t num_channels = cfg->count; + uint32_t min_buf_len = sizeof(struct iis3dwb_rtio_data); + uint64_t cycles; + int rc = 0; + uint8_t *buf; + uint32_t buf_len; + struct iis3dwb_rtio_data *edata; + struct iis3dwb_data *data = dev->data; + + /* Get the buffer for the frame, it may be allocated dynamically by the rtio context */ + rc = rtio_sqe_rx_buf(iodev_sqe, min_buf_len, min_buf_len, &buf, &buf_len); + if (rc != 0) { + LOG_ERR("Failed to get a read buffer of size %u bytes", min_buf_len); + rtio_iodev_sqe_err(iodev_sqe, -ENOMEM); + return; + } + + edata = (struct iis3dwb_rtio_data *)buf; + + edata->has_accel = 0; + edata->has_temp = 0; + + rc = sensor_clock_get_cycles(&cycles); + if (rc != 0) { + LOG_ERR("Failed to get sensor clock cycles"); + rtio_iodev_sqe_err(iodev_sqe, rc); + return; + } + + edata->header.is_fifo = false; + edata->header.range = data->range; + edata->header.timestamp = sensor_clock_cycles_to_ns(cycles); + + for (int i = 0; i < num_channels; i++) { + switch (channels[i].chan_type) { + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + case SENSOR_CHAN_ACCEL_XYZ: + edata->has_accel = 1; + + uint8_t outx_regs[] = { IIS3DWB_OUTX_L_A, }; + struct spi_buf buf_xl[] = { {(uint8_t *)edata->accel, 6} }; + + /* + * Prepare rtio enabled bus to read IIS3DWB_OUTX_L_A register + * where accelerometer data is available. + * Then iis3dwb_one_shot_complete_cb callback will be invoked. + * + * STMEMSC API equivalent code: + * + * uint8_t accel_raw[6]; + * + * iis3dwb_acceleration_raw_get(&dev_ctx, accel_raw); + */ + iis3dwb_rtio_rd_transaction(dev, + outx_regs, + ARRAY_SIZE(outx_regs), + buf_xl, + iodev_sqe, + iis3dwb_one_shot_complete_cb); + break; + +#if defined(CONFIG_IIS3DWB_ENABLE_TEMP) + case SENSOR_CHAN_DIE_TEMP: + edata->has_temp = 1; + + uint8_t outt_regs[] = { IIS3DWB_OUT_TEMP_L, }; + struct spi_buf buf_t[] = { {(uint8_t *)&edata->temp, 2} }; + + /* + * Prepare rtio enabled bus to read IIS3DWB_OUT_TEMP_L register + * where temperature data is available. + * Then iis3dwb_one_shot_complete_cb callback will be invoked. + * + * STMEMSC API equivalent code: + * + * int16_t val; + * + * iis3dwb_temperature_raw_get(&dev_ctx, &val); + */ + iis3dwb_rtio_rd_transaction(dev, + outt_regs, + ARRAY_SIZE(outt_regs), + buf_t, + iodev_sqe, + iis3dwb_one_shot_complete_cb); + break; +#endif + + default: + continue; + } + } + + if (edata->has_accel == 0 && edata->has_temp == 0) { + rtio_iodev_sqe_err(iodev_sqe, -EIO); + } +} + +void iis3dwb_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) +{ + const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data; + + if (!cfg->is_streaming) { + iis3dwb_submit_one_shot(dev, iodev_sqe); + } else if (IS_ENABLED(CONFIG_IIS3DWB_STREAM)) { + iis3dwb_submit_stream(dev, iodev_sqe); + } else { + rtio_iodev_sqe_err(iodev_sqe, -ENOTSUP); + } +} + +static DEVICE_API(sensor, iis3dwb_driver_api) = { + .attr_set = iis3dwb_attr_set, + .get_decoder = iis3dwb_get_decoder, + .submit = iis3dwb_submit, +}; + +static int iis3dwb_init_chip(const struct device *dev) +{ + const struct iis3dwb_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; + uint8_t chip_id, rst; + + if (iis3dwb_device_id_get(ctx, &chip_id) < 0) { + LOG_DBG("Failed reading chip id"); + return -EIO; + } + + if (chip_id != IIS3DWB_ID) { + LOG_DBG("Invalid chip id 0x%x", chip_id); + return -EIO; + } + + /* + * Restore default configuration + */ + iis3dwb_reset_set(ctx, PROPERTY_ENABLE); + WAIT_FOR((iis3dwb_reset_get(ctx, &rst) == 0) && !rst, + 100 * USEC_PER_MSEC, k_msleep(10)); + + /* Enable Block Data Update */ + iis3dwb_block_data_update_set(ctx, PROPERTY_ENABLE); + + return 0; +} + +static int iis3dwb_init(const struct device *dev) +{ + const struct iis3dwb_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; + int ret; + + if (iis3dwb_init_chip(dev) < 0) { + LOG_DBG("Failed to initialize chip"); + return -EIO; + } + +#ifdef CONFIG_IIS3DWB_TRIGGER + if (cfg->trig_enabled) { + if (iis3dwb_init_interrupt(dev) < 0) { + LOG_ERR("Failed to initialize interrupt."); + return -EIO; + } + } +#endif + + /* set sensor default scale (used to convert sample values) */ + LOG_DBG("%s: range is %d", dev->name, cfg->range); + ret = iis3dwb_set_range_raw(dev, cfg->range); + if (ret < 0) { + LOG_ERR("%s: range init error %d", dev->name, cfg->range); + return ret; + } + + /* set sensor filter setting */ + LOG_DBG("%s: filter is %d", dev->name, cfg->filter); + ret = iis3dwb_xl_filt_path_on_out_set(ctx, cfg->filter); + if (ret < 0) { + LOG_ERR("%s: filter init error %d", dev->name, cfg->filter); + return ret; + } + + /* set sensor default odr */ + LOG_DBG("%s: odr: %d", dev->name, cfg->odr); + ret = iis3dwb_set_odr_raw(dev, cfg->odr); + if (ret < 0) { + LOG_ERR("%s: odr init error", dev->name); + return ret; + } + + return 0; +} + +#define IIS3DWB_SPI_OPERATION \ + (SPI_WORD_SET(8) | SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA) + +#define IIS3DWB_SPI_RTIO_DEFINE(inst) \ + SPI_DT_IODEV_DEFINE(iis3dwb_iodev_##inst, \ + DT_DRV_INST(inst), IIS3DWB_SPI_OPERATION, 0U); \ + RTIO_DEFINE(iis3dwb_rtio_ctx_##inst, 8, 8); + +#ifdef CONFIG_IIS3DWB_TRIGGER +#define IIS3DWB_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 IIS3DWB_CFG_IRQ(inst) +#endif /* CONFIG_IIS3DWB_TRIGGER */ + +#define IIS3DWB_CONFIG(inst) \ + { \ + STMEMSC_CTX_SPI(&iis3dwb_config_##inst.stmemsc_cfg), \ + .stmemsc_cfg = { \ + .spi = SPI_DT_SPEC_INST_GET(inst, \ + IIS3DWB_SPI_OPERATION, 0), \ + }, \ + \ + .range = DT_INST_PROP(inst, range), \ + .filter = DT_INST_PROP(inst, filter), \ + .odr = DT_INST_PROP(inst, odr), \ + \ + IF_ENABLED(CONFIG_IIS3DWB_STREAM, \ + (.fifo_wtm = DT_INST_PROP(inst, fifo_watermark), \ + .accel_batch = DT_INST_PROP(inst, accel_fifo_batch_rate), \ + .temp_batch = DT_INST_PROP(inst, temp_fifo_batch_rate), \ + .ts_batch = DT_INST_PROP(inst, timestamp_fifo_batch_rate),)) \ + \ + IF_ENABLED(UTIL_OR(DT_INST_NODE_HAS_PROP(inst, int1_gpios), \ + DT_INST_NODE_HAS_PROP(inst, int2_gpios)), \ + (IIS3DWB_CFG_IRQ(inst))) \ + } + +#define IIS3DWB_DEFINE(inst) \ + IIS3DWB_SPI_RTIO_DEFINE(inst); \ + static struct iis3dwb_data iis3dwb_data_##inst = \ + { \ + .rtio_ctx = &iis3dwb_rtio_ctx_##inst, \ + .iodev = &iis3dwb_iodev_##inst, \ + }; \ + static const struct iis3dwb_config iis3dwb_config_##inst = \ + IIS3DWB_CONFIG(inst); \ + \ + SENSOR_DEVICE_DT_INST_DEFINE(inst, iis3dwb_init, NULL, \ + &iis3dwb_data_##inst, &iis3dwb_config_##inst, POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, &iis3dwb_driver_api); \ + +DT_INST_FOREACH_STATUS_OKAY(IIS3DWB_DEFINE) diff --git a/drivers/sensor/st/iis3dwb/iis3dwb.h b/drivers/sensor/st/iis3dwb/iis3dwb.h new file mode 100644 index 000000000000..1094d3e9f5a0 --- /dev/null +++ b/drivers/sensor/st/iis3dwb/iis3dwb.h @@ -0,0 +1,147 @@ +/* ST Microelectronics IIS3DWB accelerometer sensor + * + * Copyright (c) 2025 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://www.st.com/resource/en/datasheet/iis3dwb.pdf + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_IIS3DWB_IIS3DWB_H_ +#define ZEPHYR_DRIVERS_SENSOR_IIS3DWB_IIS3DWB_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iis3dwb_reg.h" +#include +#include + +#define GAIN_UNIT (61LL) + +struct trigger_config { + uint8_t int_fifo_th : 1; + uint8_t int_fifo_full : 1; + uint8_t int_drdy : 1; +}; + +struct iis3dwb_config { + stmdev_ctx_t ctx; + union { +#if DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(st_iis3dwb, spi) + const struct spi_dt_spec spi; +#endif + } stmemsc_cfg; + + uint8_t range; + uint8_t filter; + uint8_t odr; + +#ifdef CONFIG_IIS3DWB_STREAM + uint16_t fifo_wtm; + uint8_t accel_batch : 4; + uint8_t temp_batch : 2; + uint8_t ts_batch : 2; +#endif +#ifdef CONFIG_IIS3DWB_TRIGGER + const struct gpio_dt_spec int1_gpio; + const struct gpio_dt_spec int2_gpio; + uint8_t drdy_pulsed; + uint8_t drdy_pin; + bool trig_enabled; +#endif +}; + +struct iis3dwb_data { + int16_t acc[3]; + + uint8_t range; + uint8_t odr; + + struct rtio *rtio_ctx; + struct rtio_iodev *iodev; + struct rtio_iodev_sqe *streaming_sqe; + +#ifdef CONFIG_IIS3DWB_STREAM + uint64_t timestamp; + uint8_t status; + uint8_t fifo_status[2]; + uint16_t fifo_count; + struct trigger_config trig_cfg; + uint8_t accel_batch_odr : 4; + uint8_t temp_batch_odr : 2; + uint8_t ts_batch_odr : 2; +#endif + +#ifdef CONFIG_IIS3DWB_TRIGGER + struct gpio_dt_spec *drdy_gpio; + struct gpio_callback gpio_cb; + + const struct device *dev; +#endif /* CONFIG_IIS3DWB_TRIGGER */ +}; + +int iis3dwb_spi_init(const struct device *dev); + +#define IIS3DWB_FIFO_ITEM_LEN 7 +#define IIS3DWB_FIFO_SIZE(x) (x * IIS3DWB_FIFO_ITEM_LEN) + +#ifdef CONFIG_IIS3DWB_TRIGGER +int iis3dwb_init_interrupt(const struct device *dev); +#endif + +/* decoder */ +struct iis3dwb_decoder_header { + uint64_t timestamp; + uint8_t is_fifo: 1; + uint8_t range: 2; + uint8_t reserved: 5; + uint8_t int_status; +} __attribute__((__packed__)); + +struct iis3dwb_fifo_data { + struct iis3dwb_decoder_header header; + uint32_t accel_odr: 4; + uint32_t fifo_mode_sel: 2; + uint32_t fifo_count: 10; + uint32_t reserved_1: 5; + uint32_t accel_batch_odr: 4; + uint32_t temp_batch_odr: 2; + uint32_t ts_batch_odr: 2; + uint32_t reserved: 3; +} __attribute__((__packed__)); + +struct iis3dwb_rtio_data { + struct iis3dwb_decoder_header header; + struct { + uint8_t has_accel: 1; /* set if accel channel has data */ + uint8_t has_temp: 1; /* set if temp channel has data */ + uint8_t reserved: 6; + } __attribute__((__packed__)); + int16_t accel[3]; + int16_t temp; +}; + +int iis3dwb_encode(const struct device *dev, const struct sensor_chan_spec *const channels, + const size_t num_channels, uint8_t *buf); +int iis3dwb_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder); + +#ifdef CONFIG_IIS3DWB_TRIGGER +int iis3dwb_route_int1(const struct device *dev, iis3dwb_pin_int1_route_t pin_int); +int iis3dwb_route_int2(const struct device *dev, iis3dwb_pin_int2_route_t pin_int); +void iis3dwb_stream_irq_handler(const struct device *dev); +#endif + +void iis3dwb_rtio_rd_transaction(const struct device *dev, + uint8_t *regs, uint8_t regs_num, + struct spi_buf *buf, + struct rtio_iodev_sqe *iodev_sqe, + rtio_callback_t complete_op_cb); +#endif /* ZEPHYR_DRIVERS_SENSOR_IIS3DWB_IIS3DWB_H_ */ diff --git a/drivers/sensor/st/iis3dwb/iis3dwb_decoder.c b/drivers/sensor/st/iis3dwb/iis3dwb_decoder.c new file mode 100644 index 000000000000..5bc6e2ce3b5a --- /dev/null +++ b/drivers/sensor/st/iis3dwb/iis3dwb_decoder.c @@ -0,0 +1,434 @@ +/* ST Microelectronics IIS3DWB accelerometer sensor + * + * Copyright (c) 2025 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://www.st.com/resource/en/datasheet/iis3dwb.pdf + */ + +#include "iis3dwb.h" + +#include +LOG_MODULE_REGISTER(IIS3DWB_DECODER, CONFIG_SENSOR_LOG_LEVEL); + +#ifdef CONFIG_IIS3DWB_STREAM +static const uint32_t accel_period_ns[] = { + [IIS3DWB_DT_XL_NOT_BATCHED] = UINT32_C(0), + [IIS3DWB_DT_XL_BATCHED_AT_26k7Hz] = UINT32_C(1000000000) / 26700, +}; + +#if defined(CONFIG_IIS3DWB_ENABLE_TEMP) +static const uint32_t temp_period_ns[] = { + [IIS3DWB_DT_TEMP_NOT_BATCHED] = UINT32_C(0), + [IIS3DWB_DT_TEMP_BATCHED_AT_104Hz] = UINT32_C(1000000000) / 104, +}; +#endif +#endif /* CONFIG_IIS3DWB_STREAM */ + +/* + * Expand val to q31_t according to its range; this is achieved multiplying by 2^31/2^range. + */ +#define Q31_SHIFT_VAL(val, range) \ + (q31_t) (roundf((val) * ((int64_t)1 << (31 - (range))))) + +/* + * Expand micro_val (a generic micro unit) to q31_t according to its range; this is achieved + * multiplying by 2^31/2^range. Then transform it to val. + */ +#define Q31_SHIFT_MICROVAL(micro_val, range) \ + (q31_t) ((int64_t)(micro_val) * ((int64_t)1 << (31 - (range))) / 1000000LL) + +/* bit range for Accelerometer for a given range value */ +static const int8_t accel_range[] = { + [IIS3DWB_DT_FS_2G] = 5, + [IIS3DWB_DT_FS_4G] = 6, + [IIS3DWB_DT_FS_8G] = 7, + [IIS3DWB_DT_FS_16G] = 8, +}; + +#if defined(CONFIG_IIS3DWB_ENABLE_TEMP) +/* bit range for Temperature sensor */ +static const int8_t temp_range = 9; + +/* transform temperature LSB into micro-Celsius */ +#define SENSOR_TEMP_UCELSIUS(t_lsb) \ + (int64_t) (25000000LL + (((int64_t)(t_lsb) * 1000000LL) / 256LL)) + +#endif + +/* Calculate scaling factor to transform micro-g/LSB unit into micro-ms2/LSB */ +#define SENSOR_SCALE_UG_TO_UMS2(ug_lsb) \ + (int32_t)((ug_lsb) * SENSOR_G / 1000000LL) + +/* + * Accelerometer scaling factors table for a given range value + * GAIN_UNIT_XL is expressed in ug/LSB. + */ +static const int32_t accel_scaler[] = { + [IIS3DWB_DT_FS_2G] = SENSOR_SCALE_UG_TO_UMS2(GAIN_UNIT), + [IIS3DWB_DT_FS_4G] = SENSOR_SCALE_UG_TO_UMS2(2 * GAIN_UNIT), + [IIS3DWB_DT_FS_8G] = SENSOR_SCALE_UG_TO_UMS2(4 * GAIN_UNIT), + [IIS3DWB_DT_FS_16G] = SENSOR_SCALE_UG_TO_UMS2(8 * GAIN_UNIT), +}; + +/* Calculate scaling factor to transform micro-dps/LSB unit into micro-rads/LSB */ +#define SENSOR_SCALE_UDPS_TO_URADS(udps_lsb) \ + (int32_t)(((udps_lsb) * SENSOR_PI / 180LL) / 1000000LL) + +static int iis3dwb_decoder_get_frame_count(const uint8_t *buffer, + struct sensor_chan_spec chan_spec, + uint16_t *frame_count) +{ + struct iis3dwb_fifo_data *data = (struct iis3dwb_fifo_data *)buffer; + struct iis3dwb_rtio_data *rdata = (struct iis3dwb_rtio_data *)buffer; + const struct iis3dwb_decoder_header *header = &data->header; + + if (chan_spec.chan_idx != 0) { + return -ENOTSUP; + } + + if (!header->is_fifo) { + switch (chan_spec.chan_type) { + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + case SENSOR_CHAN_ACCEL_XYZ: + *frame_count = rdata->has_accel ? 1 : 0; + return 0; + + case SENSOR_CHAN_DIE_TEMP: + *frame_count = rdata->has_temp ? 1 : 0; + return 0; + + default: + *frame_count = 0; + return -ENOTSUP; + } + + return 0; + } + +#ifdef CONFIG_IIS3DWB_STREAM + const struct iis3dwb_fifo_data *edata = (const struct iis3dwb_fifo_data *)buffer; + const uint8_t *buffer_end; + uint8_t fifo_tag; + uint16_t tot_accel_fifo_words = 0; + +#if defined(CONFIG_IIS3DWB_ENABLE_TEMP) + uint16_t tot_temp_fifo_words = 0; +#endif + + buffer += sizeof(struct iis3dwb_fifo_data); + buffer_end = buffer + IIS3DWB_FIFO_SIZE(edata->fifo_count); + + /* count total FIFO word for each tag */ + while (buffer < buffer_end) { + fifo_tag = (buffer[0] >> 3); + + switch (fifo_tag) { + case IIS3DWB_XL_TAG: + tot_accel_fifo_words++; + break; +#if defined(CONFIG_IIS3DWB_ENABLE_TEMP) + case IIS3DWB_TEMPERATURE_TAG: + tot_temp_fifo_words++; + break; +#endif + default: + break; + } + + buffer += IIS3DWB_FIFO_ITEM_LEN; + } + + switch (chan_spec.chan_type) { + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + case SENSOR_CHAN_ACCEL_XYZ: + *frame_count = tot_accel_fifo_words; + break; + +#if defined(CONFIG_IIS3DWB_ENABLE_TEMP) + case SENSOR_CHAN_DIE_TEMP: + *frame_count = tot_temp_fifo_words; + break; +#endif + default: + *frame_count = 0; + break; + } +#endif /* CONFIG_IIS3DWB_STREAM */ + + return 0; +} + +#ifdef CONFIG_IIS3DWB_STREAM +static int iis3dwb_decode_fifo(const uint8_t *buffer, struct sensor_chan_spec chan_spec, + uint32_t *fit, uint16_t max_count, void *data_out) +{ + const struct iis3dwb_fifo_data *edata = (const struct iis3dwb_fifo_data *)buffer; + const uint8_t *buffer_end; + const struct iis3dwb_decoder_header *header = &edata->header; + int count = 0; + uint8_t fifo_tag; + uint16_t xl_count = 0; +#if defined(CONFIG_IIS3DWB_ENABLE_TEMP) + uint16_t temp_count = 0; +#endif + uint16_t tot_fifo_samples = 0; + int ret; + + /* count total FIFO word for each tag */ + ret = iis3dwb_decoder_get_frame_count(buffer, chan_spec, &tot_fifo_samples); + if (ret < 0) { + return 0; + } + + buffer += sizeof(struct iis3dwb_fifo_data); + buffer_end = buffer + IIS3DWB_FIFO_SIZE(edata->fifo_count); + + /* + * Timestamp in header is set when FIFO threshold is reached, so + * set time baseline going back in past according to total number + * of FIFO word for each type. + */ + if (SENSOR_CHANNEL_IS_ACCEL(chan_spec.chan_type)) { + ((struct sensor_data_header *)data_out)->base_timestamp_ns = + edata->header.timestamp - + (tot_fifo_samples - 1) * + accel_period_ns[edata->accel_batch_odr]; +#if defined(CONFIG_IIS3DWB_ENABLE_TEMP) + } else if (chan_spec.chan_type == SENSOR_CHAN_DIE_TEMP) { + ((struct sensor_data_header *)data_out)->base_timestamp_ns = + edata->header.timestamp - + (tot_fifo_samples - 1) * + temp_period_ns[edata->temp_batch_odr]; +#endif + } + + while (count < max_count && buffer < buffer_end) { + const uint8_t *frame_end = buffer; + uint8_t skip_frame; + + skip_frame = 0; + frame_end += IIS3DWB_FIFO_ITEM_LEN; + + fifo_tag = (buffer[0] >> 3); + + switch (fifo_tag) { + case IIS3DWB_XL_TAG: { + struct sensor_three_axis_data *out = data_out; + int16_t x, y, z; + const int32_t scale = accel_scaler[header->range]; + + xl_count++; + if ((uintptr_t)buffer < *fit) { + /* This frame was already decoded, move on to the next frame */ + buffer = frame_end; + continue; + } + + if (!SENSOR_CHANNEL_IS_ACCEL(chan_spec.chan_type)) { + buffer = frame_end; + continue; + } + + out->readings[count].timestamp_delta = + (xl_count - 1) * accel_period_ns[edata->accel_batch_odr]; + + x = *(int16_t *)&buffer[1]; + y = *(int16_t *)&buffer[3]; + z = *(int16_t *)&buffer[5]; + + out->shift = accel_range[header->range]; + + out->readings[count].x = Q31_SHIFT_MICROVAL(scale * x, out->shift); + out->readings[count].y = Q31_SHIFT_MICROVAL(scale * y, out->shift); + out->readings[count].z = Q31_SHIFT_MICROVAL(scale * z, out->shift); + break; + } + +#if defined(CONFIG_IIS3DWB_ENABLE_TEMP) + case IIS3DWB_TEMPERATURE_TAG: { + struct sensor_q31_data *out = data_out; + int16_t t; + int64_t t_uC; + + temp_count++; + if ((uintptr_t)buffer < *fit) { + /* This frame was already decoded, move on to the next frame */ + buffer = frame_end; + continue; + } + + if (chan_spec.chan_type != SENSOR_CHAN_DIE_TEMP) { + buffer = frame_end; + continue; + } + + out->readings[count].timestamp_delta = + (temp_count - 1) * temp_period_ns[edata->temp_batch_odr]; + + t = *(int16_t *)&buffer[1]; + t_uC = SENSOR_TEMP_UCELSIUS(t); + + out->shift = temp_range; + + out->readings[count].temperature = Q31_SHIFT_MICROVAL(t_uC, out->shift); + break; + } +#endif + + default: + /* skip unhandled FIFO tag */ + buffer = frame_end; + LOG_DBG("unknown FIFO tag %02x", fifo_tag); + continue; + } + + buffer = frame_end; + *fit = (uintptr_t)frame_end; + count++; + } + + return count; +} +#endif /* CONFIG_IIS3DWB_STREAM */ + +static int iis3dwb_decode_one_shot(const uint8_t *buffer, struct sensor_chan_spec chan_spec, + uint32_t *fit, uint16_t max_count, void *data_out) +{ + const struct iis3dwb_rtio_data *edata = (const struct iis3dwb_rtio_data *)buffer; + const struct iis3dwb_decoder_header *header = &edata->header; + + if (*fit != 0) { + return 0; + } + if (max_count == 0 || chan_spec.chan_idx != 0) { + return -EINVAL; + } + + switch (chan_spec.chan_type) { + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + case SENSOR_CHAN_ACCEL_XYZ: { + const int32_t scale = accel_scaler[header->range]; + + if (edata->has_accel == 0) { + return -ENODATA; + } + + struct sensor_three_axis_data *out = data_out; + + out->header.base_timestamp_ns = edata->header.timestamp; + out->header.reading_count = 1; + + out->shift = accel_range[header->range]; + + out->readings[0].x = Q31_SHIFT_MICROVAL(scale * edata->accel[0], out->shift); + out->readings[0].y = Q31_SHIFT_MICROVAL(scale * edata->accel[1], out->shift); + out->readings[0].z = Q31_SHIFT_MICROVAL(scale * edata->accel[2], out->shift); + *fit = 1; + return 1; + } +#if defined(CONFIG_IIS3DWB_ENABLE_TEMP) + case SENSOR_CHAN_DIE_TEMP: { + int64_t t_uC; + + if (edata->has_temp == 0) { + return -ENODATA; + } + + struct sensor_q31_data *out = data_out; + + out->header.base_timestamp_ns = edata->header.timestamp; + out->header.reading_count = 1; + + out->shift = temp_range; + + /* transform temperature LSB into micro-Celsius */ + t_uC = SENSOR_TEMP_UCELSIUS(edata->temp); + + out->readings[0].temperature = Q31_SHIFT_MICROVAL(t_uC, out->shift); + *fit = 1; + return 1; + } +#endif + default: + return -EINVAL; + } +} + +static int iis3dwb_decoder_decode(const uint8_t *buffer, struct sensor_chan_spec chan_spec, + uint32_t *fit, uint16_t max_count, void *data_out) +{ +#ifdef CONFIG_IIS3DWB_STREAM + const struct iis3dwb_decoder_header *header = + (const struct iis3dwb_decoder_header *)buffer; + + if (header->is_fifo) { + return iis3dwb_decode_fifo(buffer, chan_spec, fit, max_count, data_out); + } +#endif + + return iis3dwb_decode_one_shot(buffer, chan_spec, fit, max_count, data_out); +} + +static int iis3dwb_decoder_get_size_info(struct sensor_chan_spec chan_spec, size_t *base_size, + size_t *frame_size) +{ + switch (chan_spec.chan_type) { + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + case SENSOR_CHAN_ACCEL_XYZ: + *base_size = sizeof(struct sensor_three_axis_data); + *frame_size = sizeof(struct sensor_three_axis_sample_data); + return 0; + case SENSOR_CHAN_DIE_TEMP: + *base_size = sizeof(struct sensor_q31_data); + *frame_size = sizeof(struct sensor_q31_sample_data); + return 0; + default: + return -ENOTSUP; + } +} + +static bool iis3dwb_decoder_has_trigger(const uint8_t *buffer, enum sensor_trigger_type trigger) +{ +#ifdef CONFIG_IIS3DWB_STREAM + const struct iis3dwb_decoder_header *header = + (const struct iis3dwb_decoder_header *)buffer; + + switch (trigger) { + case SENSOR_TRIG_DATA_READY: + return header->int_status & 0x01; + case SENSOR_TRIG_FIFO_WATERMARK: + return header->int_status & 0x80; + case SENSOR_TRIG_FIFO_FULL: + return header->int_status & 0x20; + default: + return false; + } +#endif + return false; +} + +SENSOR_DECODER_API_DT_DEFINE() = { + .get_frame_count = iis3dwb_decoder_get_frame_count, + .get_size_info = iis3dwb_decoder_get_size_info, + .decode = iis3dwb_decoder_decode, + .has_trigger = iis3dwb_decoder_has_trigger, +}; + +int iis3dwb_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder) +{ + ARG_UNUSED(dev); + *decoder = &SENSOR_DECODER_NAME(); + + return 0; +} diff --git a/drivers/sensor/st/iis3dwb/iis3dwb_rtio.h b/drivers/sensor/st/iis3dwb/iis3dwb_rtio.h new file mode 100644 index 000000000000..329e4a8b6fdd --- /dev/null +++ b/drivers/sensor/st/iis3dwb/iis3dwb_rtio.h @@ -0,0 +1,19 @@ +/* ST Microelectronics IIS3DWB accel sensor driver + * + * Copyright (c) 2025 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_IIS3DWB_RTIO_H_ +#define ZEPHYR_DRIVERS_SENSOR_IIS3DWB_RTIO_H_ + +#include +#include +#include + +void iis3dwb_submit(const struct device *sensor, struct rtio_iodev_sqe *iodev_sqe); +void iis3dwb_submit_stream(const struct device *sensor, struct rtio_iodev_sqe *iodev_sqe); +void iis3dwb_stream_irq_handler(const struct device *dev); + +#endif /* ZEPHYR_DRIVERS_SENSOR_IIS3DWB_RTIO_H_ */ diff --git a/drivers/sensor/st/iis3dwb/iis3dwb_stream.c b/drivers/sensor/st/iis3dwb/iis3dwb_stream.c new file mode 100644 index 000000000000..722ff424313c --- /dev/null +++ b/drivers/sensor/st/iis3dwb/iis3dwb_stream.c @@ -0,0 +1,543 @@ +/* ST Microelectronics IIS3DWB accelerometer senor + * + * Copyright (c) 2025 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://www.st.com/resource/en/datasheet/iis3dwb.pdf + */ + +#include +#include +#include "iis3dwb.h" +#include +#include + +#include +LOG_MODULE_DECLARE(IIS3DWB); + +static void iis3dwb_config_fifo(const struct device *dev, struct trigger_config trig_cfg) +{ + struct iis3dwb_data *iis3dwb = dev->data; + const struct iis3dwb_config *config = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&config->ctx; + + /* disable FIFO as first thing */ + iis3dwb_fifo_watermark_set(ctx, 0); + iis3dwb_fifo_xl_batch_set(ctx, IIS3DWB_DT_XL_NOT_BATCHED); + iis3dwb_fifo_temp_batch_set(ctx, IIS3DWB_DT_TEMP_NOT_BATCHED); + iis3dwb_fifo_timestamp_batch_set(ctx, IIS3DWB_DT_TS_NOT_BATCHED); + iis3dwb_fifo_mode_set(ctx, IIS3DWB_BYPASS_MODE); + + if (trig_cfg.int_fifo_th || trig_cfg.int_fifo_full) { + iis3dwb_fifo_watermark_set(ctx, config->fifo_wtm); + iis3dwb_fifo_xl_batch_set(ctx, config->accel_batch); + iis3dwb_fifo_temp_batch_set(ctx, config->temp_batch); + iis3dwb_fifo_timestamp_batch_set(ctx, config->ts_batch); + + iis3dwb->accel_batch_odr = config->accel_batch; + iis3dwb->temp_batch_odr = config->temp_batch; + iis3dwb->ts_batch_odr = config->ts_batch; + + iis3dwb_fifo_mode_set(ctx, IIS3DWB_STREAM_MODE); + } +} + +void iis3dwb_submit_stream(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) +{ + struct iis3dwb_data *iis3dwb = dev->data; + const struct iis3dwb_config *config = dev->config; + const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data; + struct trigger_config trig_cfg = { 0 }; + bool cfg_changed = 0; + + gpio_pin_interrupt_configure_dt(iis3dwb->drdy_gpio, GPIO_INT_DISABLE); + + for (size_t i = 0; i < cfg->count; i++) { + if (cfg->triggers[i].trigger == SENSOR_TRIG_FIFO_WATERMARK) { + trig_cfg.int_fifo_th = 1; + } else if (cfg->triggers[i].trigger == SENSOR_TRIG_FIFO_FULL) { + trig_cfg.int_fifo_full = 1; + } else if (cfg->triggers[i].trigger == SENSOR_TRIG_DATA_READY) { + trig_cfg.int_drdy = 1; + } + } + + /* if any change in trig_cfg for FIFO triggers */ + if (trig_cfg.int_fifo_th != iis3dwb->trig_cfg.int_fifo_th || + trig_cfg.int_fifo_full != iis3dwb->trig_cfg.int_fifo_full) { + iis3dwb->trig_cfg.int_fifo_th = trig_cfg.int_fifo_th; + iis3dwb->trig_cfg.int_fifo_full = trig_cfg.int_fifo_full; + + /* enable/disable the FIFO */ + iis3dwb_config_fifo(dev, trig_cfg); + cfg_changed = 1; + } + + /* if any change in trig_cfg for DRDY triggers */ + if (trig_cfg.int_drdy != iis3dwb->trig_cfg.int_drdy) { + iis3dwb->trig_cfg.int_drdy = trig_cfg.int_drdy; + cfg_changed = 1; + } + + if (cfg_changed) { + /* Set pin interrupt */ + if (config->drdy_pin == 1) { + iis3dwb_pin_int1_route_t pin_int = { 0 }; + + pin_int.fifo_th = (trig_cfg.int_fifo_th) ? 1 : 0; + pin_int.fifo_full = (trig_cfg.int_fifo_full) ? 1 : 0; + pin_int.drdy_xl = (trig_cfg.int_drdy) ? 1 : 0; + iis3dwb_route_int1(dev, pin_int); + } else if (config->drdy_pin == 2) { + iis3dwb_pin_int2_route_t pin_int = { 0 }; + + pin_int.fifo_th = (trig_cfg.int_fifo_th) ? 1 : 0; + pin_int.fifo_full = (trig_cfg.int_fifo_full) ? 1 : 0; + pin_int.drdy_xl = (trig_cfg.int_drdy) ? 1 : 0; + iis3dwb_route_int2(dev, pin_int); + } + } + + iis3dwb->streaming_sqe = iodev_sqe; + + gpio_pin_interrupt_configure_dt(iis3dwb->drdy_gpio, GPIO_INT_EDGE_TO_ACTIVE); +} + +/* + * Called by bus driver to complete the sqe. + */ +static void iis3dwb_complete_op_cb(struct rtio *r, const struct rtio_sqe *sqe, void *arg) +{ + const struct device *dev = arg; + struct iis3dwb_data *iis3dwb = dev->data; + + /* + * Mark operation completed + */ + iis3dwb->streaming_sqe = NULL; + rtio_iodev_sqe_ok(sqe->userdata, 0); + gpio_pin_interrupt_configure_dt(iis3dwb->drdy_gpio, GPIO_INT_EDGE_TO_ACTIVE); +} + +/* + * Called by bus driver to complete the IIS3DWB_FIFO_STATUS read op (2 bytes). + * If FIFO threshold or FIFO full events are active it reads all FIFO entries. + */ +static void iis3dwb_read_fifo_cb(struct rtio *r, const struct rtio_sqe *sqe, void *arg) +{ + const struct device *dev = arg; + struct iis3dwb_data *iis3dwb = dev->data; + struct rtio *rtio = iis3dwb->rtio_ctx; + struct gpio_dt_spec *irq_gpio = iis3dwb->drdy_gpio; + struct rtio_iodev *iodev = iis3dwb->iodev; + struct sensor_read_config *read_config; + uint8_t fifo_th = 0, fifo_full = 0; + uint16_t fifo_count; + + /* At this point, no sqe request is queued should be considered as a bug */ + __ASSERT_NO_MSG(iis3dwb->streaming_sqe != NULL); + + read_config = (struct sensor_read_config *)iis3dwb->streaming_sqe->sqe.iodev->data; + __ASSERT_NO_MSG(read_config != NULL); + __ASSERT_NO_MSG(read_config->is_streaming == true); + + /* parse the configuration in search for any configured trigger */ + struct sensor_stream_trigger *fifo_ths_cfg = NULL; + struct sensor_stream_trigger *fifo_full_cfg = NULL; + + for (int i = 0; i < read_config->count; ++i) { + if (read_config->triggers[i].trigger == SENSOR_TRIG_FIFO_WATERMARK) { + fifo_ths_cfg = &read_config->triggers[i]; + continue; + } + + if (read_config->triggers[i].trigger == SENSOR_TRIG_FIFO_FULL) { + fifo_full_cfg = &read_config->triggers[i]; + continue; + } + } + + /* fill fifo h/w status */ + fifo_th = (iis3dwb->fifo_status[1] & 0x80) ? 1 : 0; + fifo_full = (iis3dwb->fifo_status[1] & 0x20) ? 1 : 0; + fifo_count = (uint16_t)(iis3dwb->fifo_status[0] | + (iis3dwb->fifo_status[1] & 0x3) << 8); + iis3dwb->fifo_count = fifo_count; + + bool has_fifo_ths_trig = fifo_ths_cfg != NULL && fifo_th == 1; + bool has_fifo_full_trig = fifo_full_cfg != NULL && fifo_full == 1; + + /* check if no theshold/full fifo interrupt or spurious interrupts */ + if (!has_fifo_ths_trig && !has_fifo_full_trig) { + /* complete operation with no error */ + rtio_iodev_sqe_ok(sqe->userdata, 0); + + iis3dwb->streaming_sqe = NULL; + gpio_pin_interrupt_configure_dt(irq_gpio, GPIO_INT_EDGE_TO_ACTIVE); + return; + } + + /* flush completion */ + struct rtio_cqe *cqe; + int res = 0; + + do { + cqe = rtio_cqe_consume(rtio); + if (cqe != NULL) { + if ((cqe->result < 0) && (res == 0)) { + LOG_ERR("Bus error: %d", cqe->result); + res = cqe->result; + } + rtio_cqe_release(rtio, cqe); + } + } while (cqe != NULL); + + /* Bail/cancel attempt to read sensor on any error */ + if (res != 0) { + rtio_iodev_sqe_err(iis3dwb->streaming_sqe, res); + iis3dwb->streaming_sqe = NULL; + gpio_pin_interrupt_configure_dt(irq_gpio, GPIO_INT_EDGE_TO_ACTIVE); + return; + } + + enum sensor_stream_data_opt data_opt; + + if (has_fifo_ths_trig && !has_fifo_full_trig) { + /* Only care about fifo threshold */ + data_opt = fifo_ths_cfg->opt; + } else if (!has_fifo_ths_trig && has_fifo_full_trig) { + /* Only care about fifo full */ + data_opt = fifo_full_cfg->opt; + } else { + /* Both fifo threshold and full */ + data_opt = MIN(fifo_ths_cfg->opt, fifo_full_cfg->opt); + } + + if (data_opt == SENSOR_STREAM_DATA_NOP || data_opt == SENSOR_STREAM_DATA_DROP) { + uint8_t *buf; + uint32_t buf_len; + + /* Clear streaming_sqe since we're done with the call */ + if (rtio_sqe_rx_buf(iis3dwb->streaming_sqe, sizeof(struct iis3dwb_fifo_data), + sizeof(struct iis3dwb_fifo_data), &buf, &buf_len) != 0) { + rtio_iodev_sqe_err(iis3dwb->streaming_sqe, -ENOMEM); + iis3dwb->streaming_sqe = NULL; + gpio_pin_interrupt_configure_dt(irq_gpio, GPIO_INT_EDGE_TO_ACTIVE); + return; + } + + struct iis3dwb_fifo_data *rx_data = (struct iis3dwb_fifo_data *)buf; + + memset(buf, 0, buf_len); + rx_data->header.is_fifo = 1; + rx_data->header.timestamp = iis3dwb->timestamp; + rx_data->header.int_status = iis3dwb->fifo_status[0]; + rx_data->fifo_count = 0; + rx_data->fifo_mode_sel = 0; + + /* complete request with ok */ + rtio_iodev_sqe_ok(iis3dwb->streaming_sqe, 0); + iis3dwb->streaming_sqe = NULL; + gpio_pin_interrupt_configure_dt(irq_gpio, GPIO_INT_EDGE_TO_ACTIVE); + + if (data_opt == SENSOR_STREAM_DATA_DROP) { + + /* + * Flush the FIFO by setting the mode to IIS3DWB_BYPASS_MODE + * + * STMEMSC API equivalent code: + * + * iis3dwb_fifo_mode_set(ctx, IIS3DWB_BYPASS_MODE); + */ + struct rtio_sqe *write_fifo_mode = rtio_sqe_acquire(rtio); + uint8_t iis3dwb_fifo_mode_set[] = { + IIS3DWB_FIFO_CTRL4, + IIS3DWB_BYPASS_MODE, + }; + + write_fifo_mode->flags |= RTIO_SQE_NO_RESPONSE; + rtio_sqe_prep_tiny_write(write_fifo_mode, iodev, + RTIO_PRIO_NORM, iis3dwb_fifo_mode_set, + ARRAY_SIZE(iis3dwb_fifo_mode_set), NULL); + + rtio_submit(rtio, 0); + } + + return; + } + + uint8_t *buf, *read_buf; + uint32_t buf_len, buf_avail; + uint32_t req_len = IIS3DWB_FIFO_SIZE(fifo_count) + sizeof(struct iis3dwb_fifo_data); + + if (rtio_sqe_rx_buf(iis3dwb->streaming_sqe, req_len, req_len, &buf, &buf_len) != 0) { + LOG_ERR("Failed to get buffer"); + rtio_iodev_sqe_err(iis3dwb->streaming_sqe, -ENOMEM); + iis3dwb->streaming_sqe = NULL; + gpio_pin_interrupt_configure_dt(irq_gpio, GPIO_INT_EDGE_TO_ACTIVE); + return; + } + + /* clang-format off */ + struct iis3dwb_fifo_data hdr = { + .header = { + .is_fifo = 1, + .range = iis3dwb->range, + .timestamp = iis3dwb->timestamp, + .int_status = iis3dwb->fifo_status[0], + }, + .fifo_count = fifo_count, + .accel_batch_odr = iis3dwb->accel_batch_odr, + .accel_odr = iis3dwb->odr, + }; + /* clang-format on */ + + memcpy(buf, &hdr, sizeof(hdr)); + read_buf = buf + sizeof(hdr); + buf_avail = buf_len - sizeof(hdr); + + uint8_t fifo_data_out[] = { IIS3DWB_FIFO_DATA_OUT_TAG, }; + struct spi_buf fifo_buf[] = { {read_buf, buf_avail} }; + + /* + * Prepare rtio enabled bus to read all fifo_count entries from + * IIS3DWB_FIFO_DATA_OUT_TAG. Then iis3dwb_complete_op_cb + * callback will be invoked. + * + * STMEMSC API equivalent code: + * + * num = fifo_status.fifo_level; + * + * iis3dwb_fifo_out_raw_t f_data; + * + * iis3dwb_fifo_out_multi_raw_get(&dev_ctx, &f_data, num); + * } + */ + iis3dwb_rtio_rd_transaction(dev, + fifo_data_out, + ARRAY_SIZE(fifo_data_out), + fifo_buf, + iis3dwb->streaming_sqe, + iis3dwb_complete_op_cb); +} + +/* + * Called by bus driver to complete the IIS3DWB_STATUS_REG read op. + * If drdy_xl is active it reads XL data (6 bytes) from IIS3DWB_OUTX_L_A reg. + */ +static void iis3dwb_read_status_cb(struct rtio *r, const struct rtio_sqe *sqe, void *arg) +{ + const struct device *dev = arg; + struct iis3dwb_data *iis3dwb = dev->data; + struct rtio *rtio = iis3dwb->rtio_ctx; + struct gpio_dt_spec *irq_gpio = iis3dwb->drdy_gpio; + struct sensor_read_config *read_config; + + /* At this point, no sqe request is queued should be considered as a bug */ + __ASSERT_NO_MSG(iis3dwb->streaming_sqe != NULL); + + read_config = (struct sensor_read_config *)iis3dwb->streaming_sqe->sqe.iodev->data; + __ASSERT_NO_MSG(read_config != NULL); + __ASSERT_NO_MSG(read_config->is_streaming == true); + + /* parse the configuration in search for any configured trigger */ + struct sensor_stream_trigger *data_ready = NULL; + + for (int i = 0; i < read_config->count; ++i) { + if (read_config->triggers[i].trigger == SENSOR_TRIG_DATA_READY) { + data_ready = &read_config->triggers[i]; + break; + } + } + + /* flush completion */ + struct rtio_cqe *cqe; + int res = 0; + + do { + cqe = rtio_cqe_consume(rtio); + if (cqe != NULL) { + if ((cqe->result < 0) && (res == 0)) { + LOG_ERR("Bus error: %d", cqe->result); + res = cqe->result; + } + rtio_cqe_release(rtio, cqe); + } + } while (cqe != NULL); + + /* Bail/cancel attempt to read sensor on any error */ + if (res != 0) { + rtio_iodev_sqe_err(iis3dwb->streaming_sqe, res); + iis3dwb->streaming_sqe = NULL; + return; + } + + if (data_ready->opt == SENSOR_STREAM_DATA_NOP || + data_ready->opt == SENSOR_STREAM_DATA_DROP) { + uint8_t *buf; + uint32_t buf_len; + + /* Clear streaming_sqe since we're done with the call */ + if (rtio_sqe_rx_buf(iis3dwb->streaming_sqe, sizeof(struct iis3dwb_rtio_data), + sizeof(struct iis3dwb_rtio_data), &buf, &buf_len) != 0) { + rtio_iodev_sqe_err(iis3dwb->streaming_sqe, -ENOMEM); + iis3dwb->streaming_sqe = NULL; + gpio_pin_interrupt_configure_dt(irq_gpio, GPIO_INT_EDGE_TO_ACTIVE); + return; + } + + struct iis3dwb_rtio_data *rx_data = (struct iis3dwb_rtio_data *)buf; + + memset(buf, 0, buf_len); + rx_data->header.is_fifo = 0; + rx_data->header.timestamp = iis3dwb->timestamp; + rx_data->has_accel = 0; + rx_data->has_temp = 0; + + /* complete request with ok */ + rtio_iodev_sqe_ok(iis3dwb->streaming_sqe, 0); + iis3dwb->streaming_sqe = NULL; + gpio_pin_interrupt_configure_dt(irq_gpio, GPIO_INT_EDGE_TO_ACTIVE); + } + + /* + * Read XL data + * + * iis3dwb_status_reg_t val; + * if (val.xlda) { + */ + if (iis3dwb->status & 0x1) { + uint8_t *buf, *read_buf; + uint32_t buf_len; + uint32_t req_len = 6 + sizeof(struct iis3dwb_rtio_data); + + if (rtio_sqe_rx_buf(iis3dwb->streaming_sqe, + req_len, req_len, &buf, &buf_len) != 0) { + LOG_ERR("Failed to get buffer"); + rtio_iodev_sqe_err(iis3dwb->streaming_sqe, -ENOMEM); + iis3dwb->streaming_sqe = NULL; + gpio_pin_interrupt_configure_dt(irq_gpio, GPIO_INT_EDGE_TO_ACTIVE); + return; + } + + /* clang-format off */ + struct iis3dwb_rtio_data hdr = { + .header = { + .is_fifo = 0, + .range = iis3dwb->range, + .timestamp = iis3dwb->timestamp, + .int_status = iis3dwb->status, + }, + .has_accel = 1, + .has_temp = 0, + }; + /* clang-format on */ + + memcpy(buf, &hdr, sizeof(hdr)); + read_buf = (uint8_t *)&((struct iis3dwb_rtio_data *)buf)->accel[0]; + + uint8_t drdy_data_out[] = { IIS3DWB_OUTX_L_A, }; + struct spi_buf drdy_buf[] = { {read_buf, 6} }; + + /* + * Prepare rtio enabled bus to read IIS3DWB_OUTX_L_A register + * where accelerometer data is available. + * Then iis3dwb_complete_op_cb callback will be invoked. + * + * STMEMSC API equivalent code: + * + * uint8_t accel_raw[6]; + * + * iis3dwb_acceleration_raw_get(&dev_ctx, accel_raw); + */ + iis3dwb_rtio_rd_transaction(dev, + drdy_data_out, + ARRAY_SIZE(drdy_data_out), + drdy_buf, + iis3dwb->streaming_sqe, + iis3dwb_complete_op_cb); + } +} + +/* + * Called when one of the following trigger is active: + * + * - int_fifo_th (SENSOR_TRIG_FIFO_WATERMARK) + * - int_fifo_full (SENSOR_TRIG_FIFO_FULL) + * - int_drdy (SENSOR_TRIG_DATA_READY) + */ +void iis3dwb_stream_irq_handler(const struct device *dev) +{ + struct iis3dwb_data *iis3dwb = dev->data; + uint64_t cycles; + int rc; + + if (iis3dwb->streaming_sqe == NULL) { + return; + } + + rc = sensor_clock_get_cycles(&cycles); + if (rc != 0) { + LOG_ERR("Failed to get sensor clock cycles"); + rtio_iodev_sqe_err(iis3dwb->streaming_sqe, rc); + return; + } + + /* get timestamp as soon as the irq is served */ + iis3dwb->timestamp = sensor_clock_cycles_to_ns(cycles); + + /* handle FIFO triggers */ + if (iis3dwb->trig_cfg.int_fifo_th || iis3dwb->trig_cfg.int_fifo_full) { + iis3dwb->fifo_status[0] = iis3dwb->fifo_status[1] = 0; + + uint8_t fifo_regs[] = { IIS3DWB_FIFO_STATUS1, }; + struct spi_buf buf[] = { {iis3dwb->fifo_status, 2}, }; + + + /* + * Prepare rtio enabled bus to read IIS3DWB_FIFO_STATUS1 and + * IIS3DWB_FIFO_STATUS2 registers where FIFO threshold condition and + * count are reported. Then iis3dwb_read_fifo_cb callback will be + * invoked. + * + * STMEMSC API equivalent code: + * + * iis3dwb_fifo_status_t fifo_status; + * iis3dwb_fifo_status_get(&dev_ctx, &fifo_status); + */ + iis3dwb_rtio_rd_transaction(dev, + fifo_regs, + ARRAY_SIZE(fifo_regs), + buf, + iis3dwb->streaming_sqe, + iis3dwb_read_fifo_cb); + } + + /* handle drdy trigger */ + if (iis3dwb->trig_cfg.int_drdy) { + iis3dwb->status = 0; + + uint8_t drdy_regs[] = { IIS3DWB_STATUS_REG, }; + struct spi_buf drdy_buf[] = { {&iis3dwb->status, 1}, }; + + /* + * Prepare rtio enabled bus to read IIS3DWB_STATUS_REG register + * where accelerometer and gyroscope data ready status is available. + * Then iis3dwb_read_status_cb callback will be invoked. + * + * STMEMSC API equivalent code: + * + * uint8_t val; + * + * iis3dwb_xl_flag_data_ready_get(&dev_ctx, &val); + */ + iis3dwb_rtio_rd_transaction(dev, + drdy_regs, + ARRAY_SIZE(drdy_regs), + drdy_buf, + iis3dwb->streaming_sqe, + iis3dwb_read_status_cb); + } +} diff --git a/drivers/sensor/st/iis3dwb/iis3dwb_trigger.c b/drivers/sensor/st/iis3dwb/iis3dwb_trigger.c new file mode 100644 index 000000000000..ee87c6a3d1c0 --- /dev/null +++ b/drivers/sensor/st/iis3dwb/iis3dwb_trigger.c @@ -0,0 +1,112 @@ +/* ST Microelectronics IIS3DWB accelerometer senor + * + * Copyright (c) 2025 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://www.st.com/resource/en/datasheet/iis3dwb.pdf + */ + +#define DT_DRV_COMPAT st_iis3dwb + +#include +#include +#include +#include + +#include "iis3dwb.h" + +LOG_MODULE_DECLARE(IIS3DWB, CONFIG_SENSOR_LOG_LEVEL); + +/** + * iis3dwb_route_int1 - enable selected int pin1 to generate interrupt + */ +int iis3dwb_route_int1(const struct device *dev, iis3dwb_pin_int1_route_t pin_int) +{ + const struct iis3dwb_config *config = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&config->ctx; + int ret; + + ret = iis3dwb_pin_int1_route_set(ctx, &pin_int); + if (ret < 0) { + LOG_ERR("%s: route on int1 error %d", dev->name, ret); + return ret; + } + + return 0; +} + +/** + * iis3dwb_route_int2 - enable selected int pin2 to generate interrupt + */ +int iis3dwb_route_int2(const struct device *dev, iis3dwb_pin_int2_route_t pin_int) +{ + const struct iis3dwb_config *config = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&config->ctx; + int ret; + + ret = iis3dwb_pin_int2_route_set(ctx, &pin_int); + if (ret < 0) { + LOG_ERR("%s: route on int2 error %d", dev->name, ret); + return ret; + } + + return 0; +} + +static void iis3dwb_gpio_callback(const struct device *dev, + struct gpio_callback *cb, uint32_t pins) +{ + struct iis3dwb_data *iis3dwb = + CONTAINER_OF(cb, struct iis3dwb_data, gpio_cb); + + ARG_UNUSED(pins); + + gpio_pin_interrupt_configure_dt(iis3dwb->drdy_gpio, GPIO_INT_DISABLE); + + if (IS_ENABLED(CONFIG_IIS3DWB_STREAM)) { + iis3dwb_stream_irq_handler(iis3dwb->dev); + } +} + +int iis3dwb_init_interrupt(const struct device *dev) +{ + struct iis3dwb_data *iis3dwb = dev->data; + const struct iis3dwb_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; + int ret; + + iis3dwb->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(iis3dwb->drdy_gpio)) { + LOG_ERR("Cannot get pointer to drdy_gpio device"); + return -ENODEV; + } + + iis3dwb->dev = dev; + + ret = gpio_pin_configure_dt(iis3dwb->drdy_gpio, GPIO_INPUT); + if (ret < 0) { + LOG_ERR("Could not configure gpio"); + return ret; + } + + gpio_init_callback(&iis3dwb->gpio_cb, iis3dwb_gpio_callback, BIT(iis3dwb->drdy_gpio->pin)); + + if (gpio_add_callback(iis3dwb->drdy_gpio->port, &iis3dwb->gpio_cb) < 0) { + LOG_DBG("Could not set gpio callback"); + return -EIO; + } + + /* enable drdy on int1/int2 in pulse mode */ + iis3dwb_dataready_pulsed_t drdy = (cfg->drdy_pulsed) ? IIS3DWB_DRDY_PULSED : + IIS3DWB_DRDY_LATCHED; + if (iis3dwb_data_ready_mode_set(ctx, drdy)) { + return -EIO; + } + + return gpio_pin_interrupt_configure_dt(iis3dwb->drdy_gpio, GPIO_INT_EDGE_TO_ACTIVE); +} diff --git a/dts/bindings/sensor/st,iis3dwb.yaml b/dts/bindings/sensor/st,iis3dwb.yaml new file mode 100644 index 000000000000..34c6724dbf7c --- /dev/null +++ b/dts/bindings/sensor/st,iis3dwb.yaml @@ -0,0 +1,154 @@ +# Copyright (c) 2025 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +description: | + STMicroelectronics IIS3DWB 3-axis accelerometer accessed through SPI bus + +compatible: "st,iis3dwb" + +include: [sensor-device.yaml, spi-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). + 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. + + - 1 # drdy is generated from INT1 + - 2 # drdy is generated from INT2 + + enum: [1, 2] + + drdy-pulsed: + type: boolean + description: | + Selects the pulsed mode for data-ready interrupt when enabled, + and the latched mode when disabled. + + range: + type: int + default: 0 + description: | + Range in g. Default is power-up configuration. + + - 0 # IIS3DWB_DT_FS_2G + - 1 # IIS3DWB_DT_FS_16G + - 2 # IIS3DWB_DT_FS_4G + - 3 # IIS3DWB_DT_FS_8G + + enum: [0, 1, 2, 3] + + odr: + type: int + default: 0 + description: | + Specify the default output data rate expressed in samples per second (Hz). + Default is power-down mode + + - 0 # IIS3DWB_DT_ODR_OFF + - 5 # IIS3DWB_DT_ODR_26k7Hz + + enum: [0, 5] + + filter: + type: int + default: 0x00 + description: | + Specify the filter settings. + Default is power-down mode + + - 0x00 # IIS3DWB_DT_LP_6k3Hz + - 0x10 # IIS3DWB_DT_SLOPE_ODR_DIV_4 + - 0x11 # IIS3DWB_DT_HP_ODR_DIV_10 + - 0x12 # IIS3DWB_DT_HP_ODR_DIV_20 + - 0x13 # IIS3DWB_DT_HP_ODR_DIV_45 + - 0x14 # IIS3DWB_DT_HP_ODR_DIV_100 + - 0x15 # IIS3DWB_DT_HP_ODR_DIV_200 + - 0x16 # IIS3DWB_DT_HP_ODR_DIV_400 + - 0x17 # IIS3DWB_DT_HP_ODR_DIV_800 + - 0x37 # IIS3DWB_DT_HP_REF_MODE + - 0x80 # IIS3DWB_DT_LP_ODR_DIV_4 + - 0x81 # IIS3DWB_DT_LP_ODR_DIV_10 + - 0x82 # IIS3DWB_DT_LP_ODR_DIV_20 + - 0x83 # IIS3DWB_DT_LP_ODR_DIV_45 + - 0x84 # IIS3DWB_DT_LP_ODR_DIV_100 + - 0x85 # IIS3DWB_DT_LP_ODR_DIV_200 + - 0x86 # IIS3DWB_DT_LP_ODR_DIV_400 + - 0x87 # IIS3DWB_DT_LP_ODR_DIV_800 + + enum: [0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + 0x17, 0x37, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, + 0x86, 0x87] + + fifo-watermark: + type: int + default: 32 + description: | + Specify the default FIFO watermark threshold. Every unit indicates a FIFO row (1 byte of TAG + + 6 bytes of data). (min 0; max 255) + A typical threshold value is 32 which is then used as default. + Valid range: 0 - 511 + + accel-fifo-batch-rate: + type: int + default: 0x0 + description: | + Specify the default accelerometer FIFO batch data rate expressed in Hz. + The values are taken in accordance to iis3dwb_bdr_xl_t enumerative in hal/st module. + Default is power-up configuration. + + - 0 # IIS3DWB_DT_XL_NOT_BATCHED + - 10 # IIS3DWB_DT_XL_BATCHED_AT_26k7Hz + + enum: [0, 10] + + temp-fifo-batch-rate: + type: int + default: 0x0 + description: | + Specify the default timestamp FIFO batch data rate expressed in Hz. + The values are taken in accordance to iis3dwb_odr_t_batch_t enumerative in hal/st module. + Default is power-up configuration. + + - 0 # IIS3DWB_DT_TEMP_NOT_BATCHED + - 3 # IIS3DWB_DT_TEMP_BATCHED_AT_104Hz + + enum: [0, 3] + + timestamp-fifo-batch-rate: + type: int + default: 0x0 + description: | + Specify the default timestamp FIFO batch data rate expressed as a fraction of the odr. + The values are taken in accordance to lis2dux12_dec_ts_t enumerative in hal/st module. + Default is power-up configuration. + + - 0x0 # IIS3DWB_DT_TS_NOT_BATCHED + - 0x1 # IIS3DWB_DT_DEC_TS_1 + - 0x2 # IIS3DWB_DT_DEC_TS_8 + - 0x3 # IIS3DWB_DT_DEC_TS_32 + + enum: [0x00, 0x01, 0x02, 0x03] diff --git a/include/zephyr/dt-bindings/sensor/iis3dwb.h b/include/zephyr/dt-bindings/sensor/iis3dwb.h new file mode 100644 index 000000000000..952d5c66ed11 --- /dev/null +++ b/include/zephyr/dt-bindings/sensor/iis3dwb.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2025 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_IIS3DWB_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_IIS3DWB_H_ + +#include + +/* Data rate */ +#define IIS3DWB_DT_ODR_OFF 0 +#define IIS3DWB_DT_ODR_26k7Hz 5 /* available in LP and HP mode */ + +/* Accelerometer Full-scale */ +#define IIS3DWB_DT_FS_2G 0 /* 2g (0.061 mg/LSB) */ +#define IIS3DWB_DT_FS_16G 1 /* 16g (0.488 mg/LSB) */ +#define IIS3DWB_DT_FS_4G 2 /* 4g (0.122 mg/LSB) */ +#define IIS3DWB_DT_FS_8G 3 /* 8g (0.244 mg/LSB) */ + +/* filter settings */ +#define IIS3DWB_DT_SLOPE_ODR_DIV_4 0x10 +#define IIS3DWB_DT_HP_REF_MODE 0x37 +#define IIS3DWB_DT_HP_ODR_DIV_10 0x11 +#define IIS3DWB_DT_HP_ODR_DIV_20 0x12 +#define IIS3DWB_DT_HP_ODR_DIV_45 0x13 +#define IIS3DWB_DT_HP_ODR_DIV_100 0x14 +#define IIS3DWB_DT_HP_ODR_DIV_200 0x15 +#define IIS3DWB_DT_HP_ODR_DIV_400 0x16 +#define IIS3DWB_DT_HP_ODR_DIV_800 0x17 +#define IIS3DWB_DT_LP_6k3Hz 0x00 +#define IIS3DWB_DT_LP_ODR_DIV_4 0x80 +#define IIS3DWB_DT_LP_ODR_DIV_10 0x81 +#define IIS3DWB_DT_LP_ODR_DIV_20 0x82 +#define IIS3DWB_DT_LP_ODR_DIV_45 0x83 +#define IIS3DWB_DT_LP_ODR_DIV_100 0x84 +#define IIS3DWB_DT_LP_ODR_DIV_200 0x85 +#define IIS3DWB_DT_LP_ODR_DIV_400 0x86 +#define IIS3DWB_DT_LP_ODR_DIV_800 0x87 + +/* Accelerometer FIFO batching data rate */ +#define IIS3DWB_DT_XL_NOT_BATCHED 0x0 +#define IIS3DWB_DT_XL_BATCHED_AT_26k7Hz 0xA + +/* Temperature FIFO batching data rate */ +#define IIS3DWB_DT_TEMP_NOT_BATCHED 0 +#define IIS3DWB_DT_TEMP_BATCHED_AT_104Hz 3 + +/* Accelerometer FIFO timestamp ratio */ +#define IIS3DWB_DT_TS_NOT_BATCHED 0x0 +#define IIS3DWB_DT_DEC_TS_1 0x1 +#define IIS3DWB_DT_DEC_TS_8 0x2 +#define IIS3DWB_DT_DEC_TS_32 0x3 + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_IIS3DWB_H_ */ diff --git a/tests/drivers/build_all/sensor/app.overlay b/tests/drivers/build_all/sensor/app.overlay index 5e5b3ef382b3..e0ec4c72d06f 100644 --- a/tests/drivers/build_all/sensor/app.overlay +++ b/tests/drivers/build_all/sensor/app.overlay @@ -151,7 +151,9 @@ <&test_gpio 0 0>, <&test_gpio 0 0>, <&test_gpio 0 0>, - <&test_gpio 0 0>; /* 0x34 */ + <&test_gpio 0 0>, + <&test_gpio 0 0>, + <&test_gpio 0 0>; /* 0x36 */ #include "spi.dtsi" }; diff --git a/tests/drivers/build_all/sensor/sensors_die_temp.conf b/tests/drivers/build_all/sensor/sensors_die_temp.conf index 426a32a17a71..fcd06a848c08 100644 --- a/tests/drivers/build_all/sensor/sensors_die_temp.conf +++ b/tests/drivers/build_all/sensor/sensors_die_temp.conf @@ -1,5 +1,6 @@ # zephyr-keep-sorted-start CONFIG_IIS2ICLX_ENABLE_TEMP=y +CONFIG_IIS3DWB_ENABLE_TEMP=y CONFIG_ISM330DHCX_ENABLE_TEMP=y CONFIG_LIS2DE12_ENABLE_TEMP=y CONFIG_LIS2DS12_ENABLE_TEMP=y diff --git a/tests/drivers/build_all/sensor/spi.dtsi b/tests/drivers/build_all/sensor/spi.dtsi index 18158e29db30..85dc2b1c30fa 100644 --- a/tests/drivers/build_all/sensor/spi.dtsi +++ b/tests/drivers/build_all/sensor/spi.dtsi @@ -6,6 +6,8 @@ * Application overlay for spi devices */ +#include + /**************************************** * PLEASE KEEP REG ADDRESSES SEQUENTIAL * ***************************************/ @@ -439,3 +441,14 @@ test_spi_ad2s1210: ad2s1210@35 { reset-gpios = <&test_gpio 7 (GPIO_ACTIVE_LOW)>; clock-frequency = <8192000>; }; + +test_spi_iis3dwb: iis3dwb@36 { + compatible = "st,iis3dwb"; + reg = <0x36>; + spi-max-frequency = <0>; + int1-gpios = <&test_gpio 0 0>; + int2-gpios = <&test_gpio 0 0>; + range = ; + odr = ; + filter = ; +};