From 6b38cbef11417670d86ce076c3a04591f0edbf5c Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Tue, 17 Jun 2025 11:29:18 +0200 Subject: [PATCH] rtio: add helper function `rtio_read_transaction()` Add a helper function that constructs a rtio SQE chain with the purpose to perform a bus read operation on a list of registers. Usage: uint8_t regs_list[] = { reg_addr1, reg_addr2. ... } struct rtio_reg_buf rbuf[] = {{mem_addr_1, mem_len_1}, {mem_addr_2, mem_len_2}, ... }; rtio_read_transaction(dev, regs_list, ARRAY_SIZE(regs_list), BUS_SPI, rbuf, sqe, iodev, rtio, op_cb); Signed-off-by: Armando Visconti --- doc/releases/release-notes-4.2.rst | 7 + drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.h | 13 +- .../st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c | 103 ++++++------ include/zephyr/rtio/regmap.h | 150 ++++++++++++++++++ 4 files changed, 215 insertions(+), 58 deletions(-) create mode 100644 include/zephyr/rtio/regmap.h diff --git a/doc/releases/release-notes-4.2.rst b/doc/releases/release-notes-4.2.rst index 8623ea14c897..ac55ee1f6d2e 100644 --- a/doc/releases/release-notes-4.2.rst +++ b/doc/releases/release-notes-4.2.rst @@ -233,6 +233,13 @@ New APIs and options * :kconfig:option:`CONFIG_ZPERF_SESSION_PER_THREAD` * :c:member:`zperf_upload_params.data_loader` +* RTIO + + * :c:func:`rtio_is_spi` + * :c:func:`rtio_is_cspi` + * :c:func:`rtio_is_i3c` + * :c:func:`rtio_read_regs_async` + * Sensor * :c:func:`sensor_value_to_deci` diff --git a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.h b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.h index d35b03c5e5b3..9c21ffe6dd6b 100644 --- a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.h +++ b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.h @@ -17,6 +17,7 @@ #include #include #include "lsm6dsv16x_reg.h" +#include #define DT_DRV_COMPAT_LSM6DSV16X st_lsm6dsv16x #define DT_DRV_COMPAT_LSM6DSV32X st_lsm6dsv32x @@ -171,9 +172,9 @@ struct lsm6dsv16x_data { uint16_t accel_batch_odr : 4; uint16_t gyro_batch_odr : 4; uint16_t temp_batch_odr : 2; - uint16_t bus_type : 2; /* I2C is 0, SPI is 1, I3C is 2 */ uint16_t sflp_batch_odr : 3; - uint16_t reserved : 1; + uint16_t reserved : 3; + rtio_bus_type bus_type; int32_t gbias_x_udps; int32_t gbias_y_udps; int32_t gbias_z_udps; @@ -206,13 +207,9 @@ struct lsm6dsv16x_data { }; #ifdef CONFIG_LSM6DSV16X_STREAM -#define BUS_I2C 0 -#define BUS_SPI 1 -#define BUS_I3C 2 - -static inline uint8_t lsm6dsv16x_bus_reg(struct lsm6dsv16x_data *data, uint8_t x) +static inline uint8_t lsm6dsv16x_bus_reg(rtio_bus_type bus, uint8_t addr) { - return (data->bus_type == BUS_SPI) ? x | 0x80 : x; + return (rtio_is_spi(bus)) ? addr | 0x80 : addr; } #define LSM6DSV16X_FIFO_ITEM_LEN 7 diff --git a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c index d6a4837fdb4d..4cf47d3af1cd 100644 --- a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c +++ b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c @@ -16,48 +16,6 @@ #include LOG_MODULE_DECLARE(LSM6DSV16X_RTIO); -/* - * 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 - * - * If drdy_xl is active it reads XL data (6 bytes) from LSM6DSV16X_OUTX_L_A reg. - */ -static void lsm6dsv16x_rtio_rw_transaction(const struct device *dev, uint8_t reg, - uint8_t *buf, uint32_t len, - rtio_callback_t complete_op_cb) -{ - struct lsm6dsv16x_data *lsm6dsv16x = dev->data; - struct rtio *rtio = lsm6dsv16x->rtio_ctx; - struct rtio_iodev *iodev = lsm6dsv16x->iodev; - struct rtio_sqe *write_addr = rtio_sqe_acquire(rtio); - struct rtio_sqe *read_reg = rtio_sqe_acquire(rtio); - struct rtio_sqe *complete_op = rtio_sqe_acquire(rtio); - struct rtio_iodev_sqe *sqe = lsm6dsv16x->streaming_sqe; - uint8_t reg_bus = lsm6dsv16x_bus_reg(lsm6dsv16x, reg); - - /* check we have been able to acquire sqe */ - if (write_addr == NULL || read_reg == NULL || complete_op == NULL) { - return; - } - - rtio_sqe_prep_tiny_write(write_addr, iodev, RTIO_PRIO_NORM, ®_bus, 1, NULL); - write_addr->flags = RTIO_SQE_TRANSACTION; - rtio_sqe_prep_read(read_reg, iodev, RTIO_PRIO_NORM, buf, len, NULL); - read_reg->flags = RTIO_SQE_CHAINED; - if (lsm6dsv16x->bus_type == BUS_I2C) { - read_reg->iodev_flags |= RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART; - } else if (lsm6dsv16x->bus_type == BUS_I3C) { - read_reg->iodev_flags |= RTIO_IODEV_I3C_STOP | RTIO_IODEV_I3C_RESTART; - } - - rtio_sqe_prep_callback_no_cqe(complete_op, complete_op_cb, (void *)dev, sqe); - rtio_submit(rtio, 0); -} - static void lsm6dsv16x_config_drdy(const struct device *dev, struct trigger_config trig_cfg) { const struct lsm6dsv16x_config *config = dev->config; @@ -500,6 +458,10 @@ static void lsm6dsv16x_read_fifo_cb(struct rtio *r, const struct rtio_sqe *sqe, read_buf = buf + sizeof(hdr); buf_avail = buf_len - sizeof(hdr); + uint8_t fifo_regs[] = { lsm6dsv16x_bus_reg(lsm6dsv16x->bus_type, + LSM6DSV16X_FIFO_DATA_OUT_TAG), }; + struct rtio_reg_buf rbuf[] = { {read_buf, buf_avail}, }; + /* * Prepare rtio enabled bus to read all fifo_count entries from * LSM6DSV16X_FIFO_DATA_OUT_TAG. Then lsm6dsv16x_complete_op_cb @@ -515,8 +477,15 @@ static void lsm6dsv16x_read_fifo_cb(struct rtio *r, const struct rtio_sqe *sqe, * lsm6dsv16x_fifo_out_raw_get(&dev_ctx, &f_data); * } */ - lsm6dsv16x_rtio_rw_transaction(dev, LSM6DSV16X_FIFO_DATA_OUT_TAG, - read_buf, buf_avail, lsm6dsv16x_complete_op_cb); + rtio_read_regs_async(lsm6dsv16x->rtio_ctx, + lsm6dsv16x->iodev, + fifo_regs, + ARRAY_SIZE(fifo_regs), + lsm6dsv16x->bus_type, + rbuf, + lsm6dsv16x->streaming_sqe, + dev, + lsm6dsv16x_complete_op_cb); } /* @@ -643,6 +612,10 @@ static void lsm6dsv16x_read_status_cb(struct rtio *r, const struct rtio_sqe *sqe memcpy(buf, &hdr, sizeof(hdr)); read_buf = (uint8_t *)&((struct lsm6dsv16x_rtio_data *)buf)->acc[0]; + uint8_t fifo_regs[] = { lsm6dsv16x_bus_reg(lsm6dsv16x->bus_type, + LSM6DSV16X_OUTX_L_A), }; + struct rtio_reg_buf rbuf[] = { {read_buf, 6}, }; + /* * Prepare rtio enabled bus to read LSM6DSV16X_OUTX_L_A register * where accelerometer data is available. @@ -654,8 +627,15 @@ static void lsm6dsv16x_read_status_cb(struct rtio *r, const struct rtio_sqe *sqe * * lsm6dsv16x_acceleration_raw_get(&dev_ctx, accel_raw); */ - lsm6dsv16x_rtio_rw_transaction(dev, LSM6DSV16X_OUTX_L_A, - read_buf, 6, lsm6dsv16x_complete_op_cb); + rtio_read_regs_async(lsm6dsv16x->rtio_ctx, + lsm6dsv16x->iodev, + fifo_regs, + ARRAY_SIZE(fifo_regs), + lsm6dsv16x->bus_type, + rbuf, + lsm6dsv16x->streaming_sqe, + dev, + lsm6dsv16x_complete_op_cb); } } @@ -711,6 +691,10 @@ void lsm6dsv16x_stream_irq_handler(const struct device *dev) #endif lsm6dsv16x->fifo_status[0] = lsm6dsv16x->fifo_status[1] = 0; + uint8_t fifo_regs[] = { lsm6dsv16x_bus_reg(lsm6dsv16x->bus_type, + LSM6DSV16X_FIFO_STATUS1), }; + struct rtio_reg_buf rbuf[] = { {lsm6dsv16x->fifo_status, 2}, }; + /* * Prepare rtio enabled bus to read LSM6DSV16X_FIFO_STATUS1 and * LSM6DSV16X_FIFO_STATUS2 registers where FIFO threshold condition and @@ -723,8 +707,16 @@ void lsm6dsv16x_stream_irq_handler(const struct device *dev) * * lsm6dsv16x_fifo_status_get(&dev_ctx, &fifo_status); */ - lsm6dsv16x_rtio_rw_transaction(dev, LSM6DSV16X_FIFO_STATUS1, - lsm6dsv16x->fifo_status, 2, lsm6dsv16x_read_fifo_cb); + rtio_read_regs_async(lsm6dsv16x->rtio_ctx, + lsm6dsv16x->iodev, + fifo_regs, + ARRAY_SIZE(fifo_regs), + lsm6dsv16x->bus_type, + rbuf, + lsm6dsv16x->streaming_sqe, + dev, + lsm6dsv16x_read_fifo_cb); + #if LSM6DSVXXX_ANY_INST_ON_BUS_STATUS_OKAY(i3c) } #endif @@ -734,6 +726,10 @@ void lsm6dsv16x_stream_irq_handler(const struct device *dev) if (lsm6dsv16x->trig_cfg.int_drdy) { lsm6dsv16x->status = 0; + uint8_t fifo_regs[] = { lsm6dsv16x_bus_reg(lsm6dsv16x->bus_type, + LSM6DSV16X_STATUS_REG), }; + struct rtio_reg_buf rbuf[] = { {&lsm6dsv16x->status, 1}, }; + /* * Prepare rtio enabled bus to read LSM6DSV16X_STATUS_REG register * where accelerometer and gyroscope data ready status is available. @@ -745,7 +741,14 @@ void lsm6dsv16x_stream_irq_handler(const struct device *dev) * * lsm6dsv16x_flag_data_ready_get(&dev_ctx, &drdy); */ - lsm6dsv16x_rtio_rw_transaction(dev, LSM6DSV16X_STATUS_REG, - &lsm6dsv16x->status, 1, lsm6dsv16x_read_status_cb); + rtio_read_regs_async(lsm6dsv16x->rtio_ctx, + lsm6dsv16x->iodev, + fifo_regs, + ARRAY_SIZE(fifo_regs), + lsm6dsv16x->bus_type, + rbuf, + lsm6dsv16x->streaming_sqe, + dev, + lsm6dsv16x_read_status_cb); } } diff --git a/include/zephyr/rtio/regmap.h b/include/zephyr/rtio/regmap.h new file mode 100644 index 000000000000..60cc20e4add5 --- /dev/null +++ b/include/zephyr/rtio/regmap.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2025 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_RTIO_REGMAP_H_ +#define ZEPHYR_INCLUDE_RTIO_REGMAP_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief A structure to describe a list of not-consecutive memory chunks + * for RTIO operations. + */ +struct rtio_reg_buf { + /* Valid pointer to a data buffer */ + uint8_t *bufp; + + /* Length of the buffer in bytes */ + size_t len; +}; + +/** + * @brief bus type + * + * RTIO works on top of a RTIO enabled bus, Some RTIO ops require + * a bus-related handling (e.g. rtio_read_regs_async) + */ +typedef enum { + BUS_I2C, + BUS_SPI, + BUS_I3C, +} rtio_bus_type; + +/** + * @brief chceck if bus is SPI + */ +static inline bool rtio_is_spi(rtio_bus_type bus_type) +{ + return (bus_type == BUS_SPI); +} + +/** + * @brief chceck if bus is I2C + */ +static inline bool rtio_is_i2c(rtio_bus_type bus_type) +{ + return (bus_type == BUS_I2C); +} + +/** + * @brief chceck if bus is I3C + */ +static inline bool rtio_is_i3c(rtio_bus_type bus_type) +{ + return (bus_type == BUS_I3C); +} + +/* + * @brief Create a chain of SQEs representing a bus transaction to read a reg. + * + * The RTIO-enabled bus driver is isnstrumented to perform bus read ops + * for each register in the list. + * + * Usage: + * + * uint8_t regs_list[] = { reg_addr1, reg_addr2. ... } + * struct rtio_reg_buf rbuf[] = {{mem_addr_1, mem_len_1}, + * {mem_addr_2, mem_len_2}, + * ... + * }; + * + * rtio_read_regs_async(dev, + * regs_list, + * ARRAY_SIZE(regs_list), + * BUS_SPI, + * rbuf, + * sqe, + * iodev, + * rtio, + * op_cb); + * + * @param regs pointer to list of registers to be read. Raise proper bit in case of SPI bus + * @param rtio_bus_type bus type (i2c, spi, i3c) + * @param rtio_reg_buf buffer of memory chunks + * @param iodev_sqe IODEV submission for the await op + * @param iodev IO device + * @param r RTIO context + * @param rtio_callback_t callback routine at the end of op + */ +static inline void rtio_read_regs_async(struct rtio *r, + struct rtio_iodev *iodev, + uint8_t *regs, + uint8_t regs_num, + rtio_bus_type bus_type, + struct rtio_reg_buf *buf, + struct rtio_iodev_sqe *iodev_sqe, + const struct device *dev, + rtio_callback_t complete_op_cb) +{ + 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(r); + read_reg = rtio_sqe_acquire(r); + + if (write_addr == NULL || read_reg == NULL) { + rtio_iodev_sqe_err(iodev_sqe, -ENOMEM); + rtio_sqe_drop_all(r); + return; + } + + 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].bufp, buf[i].len, NULL); + read_reg->flags = RTIO_SQE_CHAINED; + + if (rtio_is_i2c(bus_type)) { + read_reg->iodev_flags |= RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART; + } else if (rtio_is_i3c(bus_type)) { + read_reg->iodev_flags |= RTIO_IODEV_I3C_STOP | RTIO_IODEV_I3C_RESTART; + } + } + + complete_op = rtio_sqe_acquire(r); + if (complete_op == NULL) { + rtio_iodev_sqe_err(iodev_sqe, -ENOMEM); + rtio_sqe_drop_all(r); + return; + } + + rtio_sqe_prep_callback_no_cqe(complete_op, complete_op_cb, (void *)dev, iodev_sqe); + + rtio_submit(r, 0); +} + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_RTIO_REGMAP_H_ */