From 792b7e3570373efab2f4e746f47ea0abffb2b6ee 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: struct rtio_regs regs; struct rtio_reg_list regs_list[] = {{regs_addr1, mem_addr_1, mem_len_1}, {regs_addr2, mem_addr_2, mem_len_2}, ... }; regs.rtio_regs_list = regs_list; regs.rtio_regs_num = ARRAY_SIZE(regs_list); rtio_read_regs_async(rtio, iodev, RTIO_BUS_SPI, ®s, sqe, dev, op_cb); Signed-off-by: Armando Visconti --- doc/releases/release-notes-4.3.rst | 7 + drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c | 6 +- drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.h | 13 +- .../st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c | 118 ++++++------ include/zephyr/rtio/regmap.h | 169 ++++++++++++++++++ 5 files changed, 251 insertions(+), 62 deletions(-) create mode 100644 include/zephyr/rtio/regmap.h diff --git a/doc/releases/release-notes-4.3.rst b/doc/releases/release-notes-4.3.rst index 056b002a426..2f52ffae02a 100644 --- a/doc/releases/release-notes-4.3.rst +++ b/doc/releases/release-notes-4.3.rst @@ -48,6 +48,13 @@ https://docs.zephyrproject.org/latest/security/vulnerabilities.html API Changes *********** +* RTIO + + * :c:func:`rtio_is_spi` + * :c:func:`rtio_is_cspi` + * :c:func:`rtio_is_i3c` + * :c:func:`rtio_read_regs_async` + Removed APIs and options ======================== diff --git a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c index 855112bf0ee..c34ece29af8 100644 --- a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c +++ b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c @@ -1331,7 +1331,7 @@ static int lsm6dsv16x_pm_action(const struct device *dev, enum pm_device_action CONFIG_SPI_RTIO), \ (.rtio_ctx = &prefix##_rtio_ctx_##inst, \ .iodev = &prefix##_iodev_##inst, \ - .bus_type = BUS_SPI,)) \ + .bus_type = RTIO_BUS_SPI,)) \ }; \ static const struct lsm6dsv16x_config prefix##_config_##inst = \ LSM6DSV16X_CONFIG_SPI(inst, prefix); @@ -1362,7 +1362,7 @@ static int lsm6dsv16x_pm_action(const struct device *dev, enum pm_device_action CONFIG_I2C_RTIO), \ (.rtio_ctx = &prefix##_rtio_ctx_##inst, \ .iodev = &prefix##_iodev_##inst, \ - .bus_type = BUS_I2C,)) \ + .bus_type = RTIO_BUS_I2C,)) \ }; \ static const struct lsm6dsv16x_config prefix##_config_##inst = \ LSM6DSV16X_CONFIG_I2C(inst, prefix); @@ -1398,7 +1398,7 @@ static int lsm6dsv16x_pm_action(const struct device *dev, enum pm_device_action CONFIG_I3C_RTIO), \ (.rtio_ctx = &prefix##_rtio_ctx_##inst, \ .iodev = &prefix##_i3c_iodev_##inst, \ - .bus_type = BUS_I3C,)) \ + .bus_type = RTIO_BUS_I3C,)) \ }; \ static const struct lsm6dsv16x_config prefix##_config_##inst = \ LSM6DSV16X_CONFIG_I3C(inst, prefix); diff --git a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.h b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.h index d35b03c5e5b..9c21ffe6dd6 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 05f862112ff..25d725909e0 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,19 @@ 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 reg_addr = lsm6dsv16x_bus_reg(lsm6dsv16x->bus_type, LSM6DSV16X_FIFO_DATA_OUT_TAG); + struct rtio_regs fifo_regs; + struct rtio_regs_list regs_list[] = { + { + reg_addr, + read_buf, + buf_avail, + }, + }; + + fifo_regs.rtio_regs_list = regs_list; + fifo_regs.rtio_regs_num = ARRAY_SIZE(regs_list); + /* * Prepare rtio enabled bus to read all fifo_count entries from * LSM6DSV16X_FIFO_DATA_OUT_TAG. Then lsm6dsv16x_complete_op_cb @@ -515,8 +486,8 @@ 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, lsm6dsv16x->bus_type, + &fifo_regs, lsm6dsv16x->streaming_sqe, dev, lsm6dsv16x_complete_op_cb); } /* @@ -646,6 +617,19 @@ 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 reg_addr = lsm6dsv16x_bus_reg(lsm6dsv16x->bus_type, LSM6DSV16X_OUTX_L_A); + struct rtio_regs fifo_regs; + struct rtio_regs_list regs_list[] = { + { + reg_addr, + read_buf, + 6, + }, + }; + + fifo_regs.rtio_regs_list = regs_list; + fifo_regs.rtio_regs_num = ARRAY_SIZE(regs_list); + /* * Prepare rtio enabled bus to read LSM6DSV16X_OUTX_L_A register * where accelerometer data is available. @@ -657,8 +641,9 @@ 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, lsm6dsv16x->bus_type, + &fifo_regs, lsm6dsv16x->streaming_sqe, dev, + lsm6dsv16x_complete_op_cb); } } @@ -708,12 +693,26 @@ void lsm6dsv16x_stream_irq_handler(const struct device *dev) struct rtio_sqe *check_fifo_status_reg = rtio_sqe_acquire(rtio); rtio_sqe_prep_callback_no_cqe(check_fifo_status_reg, - lsm6dsv16x_read_fifo_cb, (void *)dev, NULL); + lsm6dsv16x_read_fifo_cb, (void *)dev, NULL); rtio_submit(rtio, 0); } else { #endif lsm6dsv16x->fifo_status[0] = lsm6dsv16x->fifo_status[1] = 0; + uint8_t reg_addr = + lsm6dsv16x_bus_reg(lsm6dsv16x->bus_type, LSM6DSV16X_FIFO_STATUS1); + struct rtio_regs fifo_regs; + struct rtio_regs_list regs_list[] = { + { + reg_addr, + lsm6dsv16x->fifo_status, + 2, + }, + }; + + fifo_regs.rtio_regs_list = regs_list; + fifo_regs.rtio_regs_num = ARRAY_SIZE(regs_list); + /* * Prepare rtio enabled bus to read LSM6DSV16X_FIFO_STATUS1 and * LSM6DSV16X_FIFO_STATUS2 registers where FIFO threshold condition and @@ -726,8 +725,11 @@ 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, + lsm6dsv16x->bus_type, &fifo_regs, + lsm6dsv16x->streaming_sqe, dev, + lsm6dsv16x_read_fifo_cb); + #if LSM6DSVXXX_ANY_INST_ON_BUS_STATUS_OKAY(i3c) } #endif @@ -737,6 +739,19 @@ void lsm6dsv16x_stream_irq_handler(const struct device *dev) if (lsm6dsv16x->trig_cfg.int_drdy) { lsm6dsv16x->status = 0; + uint8_t reg_addr = lsm6dsv16x_bus_reg(lsm6dsv16x->bus_type, LSM6DSV16X_STATUS_REG); + struct rtio_regs fifo_regs; + struct rtio_regs_list regs_list[] = { + { + reg_addr, + &lsm6dsv16x->status, + 1, + }, + }; + + fifo_regs.rtio_regs_list = regs_list; + fifo_regs.rtio_regs_num = ARRAY_SIZE(regs_list); + /* * Prepare rtio enabled bus to read LSM6DSV16X_STATUS_REG register * where accelerometer and gyroscope data ready status is available. @@ -748,7 +763,8 @@ 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, lsm6dsv16x->bus_type, + &fifo_regs, 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 00000000000..16018f78608 --- /dev/null +++ b/include/zephyr/rtio/regmap.h @@ -0,0 +1,169 @@ +/* + * 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_regs { + /** Number of registers in the list **/ + size_t rtio_regs_num; + + struct rtio_regs_list { + /** Register address **/ + uint8_t reg_addr; + + /** Valid pointer to a data buffer **/ + uint8_t *bufp; + + /** Length of the buffer in bytes **/ + size_t len; + } *rtio_regs_list; +}; + +/** + * @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 { + RTIO_BUS_I2C, + RTIO_BUS_SPI, + RTIO_BUS_I3C, +} rtio_bus_type; + +/** + * @brief check if bus is SPI + * @param bus_type Type of bus (I2C, SPI, I3C) + * @return true if bus type is SPI + */ +static inline bool rtio_is_spi(rtio_bus_type bus_type) +{ + return (bus_type == RTIO_BUS_SPI); +} + +/** + * @brief check if bus is I2C + * @param bus_type Type of bus (I2C, SPI, I3C) + * @return true if bus type is I2C + */ +static inline bool rtio_is_i2c(rtio_bus_type bus_type) +{ + return (bus_type == RTIO_BUS_I2C); +} + +/** + * @brief check if bus is I3C + * @param bus_type Type of bus (I2C, SPI, I3C) + * @return true if bus type is I3C + */ +static inline bool rtio_is_i3c(rtio_bus_type bus_type) +{ + return (bus_type == RTIO_BUS_I3C); +} + +/* + * @brief Create a chain of SQEs representing a bus transaction to read a reg. + * + * The RTIO-enabled bus driver is instrumented to perform bus read ops + * for each register in the list. + * + * Usage: + * + * @code{.c} + * struct rtio_regs regs; + * struct rtio_reg_list regs_list[] = {{regs_addr1, mem_addr_1, mem_len_1}, + * {regs_addr2, mem_addr_2, mem_len_2}, + * ... + * }; + * regs.rtio_regs_list = regs_list; + * regs.rtio_regs_num = ARRAY_SIZE(regs_list); + * + * rtio_read_regs_async(rtio, + * iodev, + * RTIO_BUS_SPI, + * ®s, + * sqe, + * dev, + * op_cb); + * @endcode + * + * @param r RTIO context + * @param iodev IO device + * @param bus_type Type of bus (I2C, SPI, I3C) + * @param regs pointer to list of registers to be read. Raise proper bit in case of SPI bus + * @param iodev_sqe IODEV submission for the await op + * @param dev pointer to the device structure + * @param complete_op_cb callback routine at the end of op + */ +static inline void rtio_read_regs_async(struct rtio *r, struct rtio_iodev *iodev, + rtio_bus_type bus_type, struct rtio_regs *regs, + 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; + + for (uint8_t i = 0; i < regs->rtio_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->rtio_regs_list[i].reg_addr, 1, NULL); + write_addr->flags = RTIO_SQE_TRANSACTION; + + rtio_sqe_prep_read(read_reg, iodev, RTIO_PRIO_NORM, regs->rtio_regs_list[i].bufp, + regs->rtio_regs_list[i].len, NULL); + read_reg->flags = RTIO_SQE_CHAINED; + + switch (bus_type) { + case RTIO_BUS_I2C: + read_reg->iodev_flags |= RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART; + break; + case RTIO_BUS_I3C: + read_reg->iodev_flags |= RTIO_IODEV_I3C_STOP | RTIO_IODEV_I3C_RESTART; + break; + case RTIO_BUS_SPI: + default: + break; + } + } + + 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_ */