From b848b049107d3f90149d6a5e81890d4a82a9fdd7 Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Wed, 21 May 2025 15:18:31 +0200 Subject: [PATCH 1/2] drivers/sensor/st: add support to IIS3DWB accel The IIS3DWB is a system-in-package featuring a 3-axis digital vibration sensor with low noise over an ultrawide and flat frequency range. The wide bandwidth, low noise, very stable and repeatable sensitivity, together with the capability of operating over an extended temperature range (up to +105 C), make the device particularly suitable for vibration monitoring in industrial applications. Datasheet: https://www.st.com/en/mems-and-sensors/iis3dwb.html This driver is currently only supporting the polling-mode read_and_decode APIs (both blocking and non-blocking). This driver is based on stmemsc HAL i/f v2.9.1. Signed-off-by: Armando Visconti --- drivers/sensor/st/CMakeLists.txt | 1 + drivers/sensor/st/Kconfig | 1 + drivers/sensor/st/iis3dwb/CMakeLists.txt | 12 + drivers/sensor/st/iis3dwb/Kconfig | 25 + drivers/sensor/st/iis3dwb/iis3dwb.c | 436 ++++++++++++++++++ drivers/sensor/st/iis3dwb/iis3dwb.h | 95 ++++ drivers/sensor/st/iis3dwb/iis3dwb_decoder.c | 205 ++++++++ drivers/sensor/st/iis3dwb/iis3dwb_rtio.h | 19 + dts/bindings/sensor/st,iis3dwb.yaml | 98 ++++ include/zephyr/dt-bindings/sensor/iis3dwb.h | 41 ++ tests/drivers/build_all/sensor/app.overlay | 4 +- .../build_all/sensor/sensors_die_temp.conf | 1 + tests/drivers/build_all/sensor/spi.dtsi | 13 + 13 files changed, 950 insertions(+), 1 deletion(-) create mode 100644 drivers/sensor/st/iis3dwb/CMakeLists.txt create mode 100644 drivers/sensor/st/iis3dwb/Kconfig create mode 100644 drivers/sensor/st/iis3dwb/iis3dwb.c create mode 100644 drivers/sensor/st/iis3dwb/iis3dwb.h create mode 100644 drivers/sensor/st/iis3dwb/iis3dwb_decoder.c create mode 100644 drivers/sensor/st/iis3dwb/iis3dwb_rtio.h create mode 100644 dts/bindings/sensor/st,iis3dwb.yaml create mode 100644 include/zephyr/dt-bindings/sensor/iis3dwb.h 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..01bd2c6ef284 --- /dev/null +++ b/drivers/sensor/st/iis3dwb/CMakeLists.txt @@ -0,0 +1,12 @@ +# 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_SENSOR_ASYNC_API iis3dwb_decoder.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..5b7429b6ea65 --- /dev/null +++ b/drivers/sensor/st/iis3dwb/Kconfig @@ -0,0 +1,25 @@ +# 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_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..a5c4a2b16520 --- /dev/null +++ b/drivers/sensor/st/iis3dwb/iis3dwb.c @@ -0,0 +1,436 @@ +/* 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: + 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, uint8_t fs) +{ + int ret; + uint8_t range; + + switch (fs) { + case 2: + range = IIS3DWB_DT_FS_2G; + break; + case 4: + range = IIS3DWB_DT_FS_4G; + break; + case 8: + range = IIS3DWB_DT_FS_8G; + break; + case 16: + range = IIS3DWB_DT_FS_16G; + break; + default: + 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) { + 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) { + 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); + 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) { + rtio_iodev_sqe_err(iodev_sqe, -EIO); + } +} + +void iis3dwb_submit_sync(struct rtio_iodev_sqe *iodev_sqe) +{ + const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data; + const struct device *dev = cfg->sensor; + + if (!cfg->is_streaming) { + iis3dwb_submit_one_shot(dev, iodev_sqe); + } else { + rtio_iodev_sqe_err(iodev_sqe, -ENOTSUP); + } +} + +void iis3dwb_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) +{ + struct rtio_work_req *req = rtio_work_req_alloc(); + + if (req == NULL) { + LOG_ERR("RTIO work item allocation failed. Consider to increase " + "CONFIG_RTIO_WORKQ_POOL_ITEMS."); + rtio_iodev_sqe_err(iodev_sqe, -ENOMEM); + return; + } + + rtio_work_req_submit(req, iodev_sqe, iis3dwb_submit_sync); +} + +static DEVICE_API(sensor, iis3dwb_driver_api) = { + .attr_set = iis3dwb_attr_set, +#ifdef CONFIG_SENSOR_ASYNC_API + .get_decoder = iis3dwb_get_decoder, + .submit = iis3dwb_submit, +#endif +}; + +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); + do { + iis3dwb_reset_get(ctx, &rst); + } while (rst); + + /* 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; + } + + /* 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); + +#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), \ + } + +#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..8c3c891f6f12 --- /dev/null +++ b/drivers/sensor/st/iis3dwb/iis3dwb.h @@ -0,0 +1,95 @@ +/* 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 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; +}; + +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; +}; + +int iis3dwb_spi_init(const struct device *dev); + +/* 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: 7; + uint32_t reserved_1: 5; + uint32_t accel_batch_odr: 3; + uint32_t ts_batch_odr: 2; + uint32_t reserved: 9; +} __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); + +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..61eca88f7267 --- /dev/null +++ b/drivers/sensor/st/iis3dwb/iis3dwb_decoder.c @@ -0,0 +1,205 @@ +/* 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); + +/* + * 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) / 355LL)) + +#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; + } + + return 0; +} + +static int iis3dwb_decode_sample(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) +{ + return iis3dwb_decode_sample(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; + } +} + +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, +}; + +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/dts/bindings/sensor/st,iis3dwb.yaml b/dts/bindings/sensor/st,iis3dwb.yaml new file mode 100644 index 000000000000..e43626ffc4fd --- /dev/null +++ b/dts/bindings/sensor/st,iis3dwb.yaml @@ -0,0 +1,98 @@ +# 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] + + 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] diff --git a/include/zephyr/dt-bindings/sensor/iis3dwb.h b/include/zephyr/dt-bindings/sensor/iis3dwb.h new file mode 100644 index 000000000000..8ed3f6a1693a --- /dev/null +++ b/include/zephyr/dt-bindings/sensor/iis3dwb.h @@ -0,0 +1,41 @@ +/* + * 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 + +#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 = ; +}; From f0010b26c3af29d4d3938d2524214bfd5390d045 Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Wed, 14 May 2025 11:06:15 +0200 Subject: [PATCH 2/2] drivers/sensor: iis3dwb: add streaming capabality Add read_and_decode streaming APIs support. Triggers supported: - SENSOR_TRIG_FIFO_WATERMARK - SENSOR_TRIG_FIFO_FULL - SENSOR_TRIG_DATA_READY Signed-off-by: Armando Visconti --- drivers/sensor/st/iis3dwb/CMakeLists.txt | 2 + drivers/sensor/st/iis3dwb/Kconfig | 13 +- drivers/sensor/st/iis3dwb/iis3dwb.c | 85 +-- drivers/sensor/st/iis3dwb/iis3dwb.h | 60 ++- drivers/sensor/st/iis3dwb/iis3dwb_decoder.c | 235 ++++++++- drivers/sensor/st/iis3dwb/iis3dwb_stream.c | 543 ++++++++++++++++++++ drivers/sensor/st/iis3dwb/iis3dwb_trigger.c | 112 ++++ dts/bindings/sensor/st,iis3dwb.yaml | 56 ++ include/zephyr/dt-bindings/sensor/iis3dwb.h | 14 + 9 files changed, 1079 insertions(+), 41 deletions(-) create mode 100644 drivers/sensor/st/iis3dwb/iis3dwb_stream.c create mode 100644 drivers/sensor/st/iis3dwb/iis3dwb_trigger.c diff --git a/drivers/sensor/st/iis3dwb/CMakeLists.txt b/drivers/sensor/st/iis3dwb/CMakeLists.txt index 01bd2c6ef284..160b69304c46 100644 --- a/drivers/sensor/st/iis3dwb/CMakeLists.txt +++ b/drivers/sensor/st/iis3dwb/CMakeLists.txt @@ -7,6 +7,8 @@ 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 index 5b7429b6ea65..33bd9b9a58e2 100644 --- a/drivers/sensor/st/iis3dwb/Kconfig +++ b/drivers/sensor/st/iis3dwb/Kconfig @@ -16,10 +16,21 @@ menuconfig IIS3DWB 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 index a5c4a2b16520..d4295c7dcd03 100644 --- a/drivers/sensor/st/iis3dwb/iis3dwb.c +++ b/drivers/sensor/st/iis3dwb/iis3dwb.c @@ -52,6 +52,11 @@ static int iis3dwb_odr_set(const struct device *dev, 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; } @@ -64,25 +69,20 @@ static int iis3dwb_odr_set(const struct device *dev, return 0; } -static int iis3dwb_set_fs(const struct device *dev, uint8_t fs) +static int iis3dwb_set_fs(const struct device *dev, int32_t fs) { int ret; uint8_t range; - switch (fs) { - case 2: + if (fs <= 2) { range = IIS3DWB_DT_FS_2G; - break; - case 4: + } else if (fs <= 4) { range = IIS3DWB_DT_FS_4G; - break; - case 8: + } else if (fs <= 8) { range = IIS3DWB_DT_FS_8G; - break; - case 16: + } else if (fs <= 16) { range = IIS3DWB_DT_FS_16G; - break; - default: + } else { LOG_ERR("fs [%d] not supported.", fs); return -EINVAL; } @@ -172,6 +172,8 @@ void iis3dwb_rtio_rd_transaction(const struct device *dev, 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; } @@ -185,6 +187,8 @@ void iis3dwb_rtio_rd_transaction(const struct device *dev, complete_op = rtio_sqe_acquire(rtio); if (complete_op == NULL) { + rtio_iodev_sqe_err(iodev_sqe, -ENOMEM); + rtio_sqe_drop_all(rtio); return; } @@ -210,6 +214,7 @@ static void iis3dwb_submit_one_shot(const struct device *dev, struct rtio_iodev_ 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; } @@ -291,43 +296,28 @@ static void iis3dwb_submit_one_shot(const struct device *dev, struct rtio_iodev_ } } - if (edata->has_accel == 0) { + if (edata->has_accel == 0 && edata->has_temp == 0) { rtio_iodev_sqe_err(iodev_sqe, -EIO); } } -void iis3dwb_submit_sync(struct rtio_iodev_sqe *iodev_sqe) +void iis3dwb_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) { const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data; - const struct device *dev = cfg->sensor; 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); } } -void iis3dwb_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) -{ - struct rtio_work_req *req = rtio_work_req_alloc(); - - if (req == NULL) { - LOG_ERR("RTIO work item allocation failed. Consider to increase " - "CONFIG_RTIO_WORKQ_POOL_ITEMS."); - rtio_iodev_sqe_err(iodev_sqe, -ENOMEM); - return; - } - - rtio_work_req_submit(req, iodev_sqe, iis3dwb_submit_sync); -} - static DEVICE_API(sensor, iis3dwb_driver_api) = { .attr_set = iis3dwb_attr_set, -#ifdef CONFIG_SENSOR_ASYNC_API .get_decoder = iis3dwb_get_decoder, .submit = iis3dwb_submit, -#endif }; static int iis3dwb_init_chip(const struct device *dev) @@ -350,9 +340,8 @@ static int iis3dwb_init_chip(const struct device *dev) * Restore default configuration */ iis3dwb_reset_set(ctx, PROPERTY_ENABLE); - do { - iis3dwb_reset_get(ctx, &rst); - } while (rst); + 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); @@ -371,6 +360,15 @@ static int iis3dwb_init(const struct device *dev) 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); @@ -406,6 +404,17 @@ static int iis3dwb_init(const struct device *dev) 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), \ @@ -417,6 +426,16 @@ static int iis3dwb_init(const struct device *dev) .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) \ diff --git a/drivers/sensor/st/iis3dwb/iis3dwb.h b/drivers/sensor/st/iis3dwb/iis3dwb.h index 8c3c891f6f12..1094d3e9f5a0 100644 --- a/drivers/sensor/st/iis3dwb/iis3dwb.h +++ b/drivers/sensor/st/iis3dwb/iis3dwb.h @@ -19,12 +19,19 @@ #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 { @@ -36,6 +43,20 @@ struct iis3dwb_config { 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 { @@ -47,10 +68,35 @@ struct iis3dwb_data { 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; @@ -64,11 +110,12 @@ struct iis3dwb_fifo_data { struct iis3dwb_decoder_header header; uint32_t accel_odr: 4; uint32_t fifo_mode_sel: 2; - uint32_t fifo_count: 7; + uint32_t fifo_count: 10; uint32_t reserved_1: 5; - uint32_t accel_batch_odr: 3; + uint32_t accel_batch_odr: 4; + uint32_t temp_batch_odr: 2; uint32_t ts_batch_odr: 2; - uint32_t reserved: 9; + uint32_t reserved: 3; } __attribute__((__packed__)); struct iis3dwb_rtio_data { @@ -84,9 +131,14 @@ struct iis3dwb_rtio_data { 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, diff --git a/drivers/sensor/st/iis3dwb/iis3dwb_decoder.c b/drivers/sensor/st/iis3dwb/iis3dwb_decoder.c index 61eca88f7267..5bc6e2ce3b5a 100644 --- a/drivers/sensor/st/iis3dwb/iis3dwb_decoder.c +++ b/drivers/sensor/st/iis3dwb/iis3dwb_decoder.c @@ -13,6 +13,20 @@ #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. */ @@ -40,7 +54,7 @@ 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) / 355LL)) + (int64_t) (25000000LL + (((int64_t)(t_lsb) * 1000000LL) / 256LL)) #endif @@ -96,10 +110,195 @@ static int iis3dwb_decoder_get_frame_count(const uint8_t *buffer, 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; } -static int iis3dwb_decode_sample(const uint8_t *buffer, struct sensor_chan_spec chan_spec, +#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; @@ -167,7 +366,16 @@ static int iis3dwb_decode_sample(const uint8_t *buffer, struct sensor_chan_spec 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) { - return iis3dwb_decode_sample(buffer, chan_spec, fit, max_count, 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, @@ -190,10 +398,31 @@ static int iis3dwb_decoder_get_size_info(struct sensor_chan_spec chan_spec, size } } +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) 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 index e43626ffc4fd..34c6724dbf7c 100644 --- a/dts/bindings/sensor/st,iis3dwb.yaml +++ b/dts/bindings/sensor/st,iis3dwb.yaml @@ -42,6 +42,12 @@ properties: 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 @@ -96,3 +102,53 @@ properties: 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 index 8ed3f6a1693a..952d5c66ed11 100644 --- a/include/zephyr/dt-bindings/sensor/iis3dwb.h +++ b/include/zephyr/dt-bindings/sensor/iis3dwb.h @@ -38,4 +38,18 @@ #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_ */