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,
                       &regs,
                       sqe,
                       dev,
                       op_cb);

Signed-off-by: Armando Visconti <armando.visconti@st.com>
This commit is contained in:
Armando Visconti 2025-06-17 11:29:18 +02:00 committed by Chris Friedt
parent 37d8ec295b
commit 792b7e3570
5 changed files with 251 additions and 62 deletions

View File

@ -48,6 +48,13 @@ https://docs.zephyrproject.org/latest/security/vulnerabilities.html
API Changes 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 Removed APIs and options
======================== ========================

View File

@ -1331,7 +1331,7 @@ static int lsm6dsv16x_pm_action(const struct device *dev, enum pm_device_action
CONFIG_SPI_RTIO), \ CONFIG_SPI_RTIO), \
(.rtio_ctx = &prefix##_rtio_ctx_##inst, \ (.rtio_ctx = &prefix##_rtio_ctx_##inst, \
.iodev = &prefix##_iodev_##inst, \ .iodev = &prefix##_iodev_##inst, \
.bus_type = BUS_SPI,)) \ .bus_type = RTIO_BUS_SPI,)) \
}; \ }; \
static const struct lsm6dsv16x_config prefix##_config_##inst = \ static const struct lsm6dsv16x_config prefix##_config_##inst = \
LSM6DSV16X_CONFIG_SPI(inst, prefix); 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), \ CONFIG_I2C_RTIO), \
(.rtio_ctx = &prefix##_rtio_ctx_##inst, \ (.rtio_ctx = &prefix##_rtio_ctx_##inst, \
.iodev = &prefix##_iodev_##inst, \ .iodev = &prefix##_iodev_##inst, \
.bus_type = BUS_I2C,)) \ .bus_type = RTIO_BUS_I2C,)) \
}; \ }; \
static const struct lsm6dsv16x_config prefix##_config_##inst = \ static const struct lsm6dsv16x_config prefix##_config_##inst = \
LSM6DSV16X_CONFIG_I2C(inst, prefix); 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), \ CONFIG_I3C_RTIO), \
(.rtio_ctx = &prefix##_rtio_ctx_##inst, \ (.rtio_ctx = &prefix##_rtio_ctx_##inst, \
.iodev = &prefix##_i3c_iodev_##inst, \ .iodev = &prefix##_i3c_iodev_##inst, \
.bus_type = BUS_I3C,)) \ .bus_type = RTIO_BUS_I3C,)) \
}; \ }; \
static const struct lsm6dsv16x_config prefix##_config_##inst = \ static const struct lsm6dsv16x_config prefix##_config_##inst = \
LSM6DSV16X_CONFIG_I3C(inst, prefix); LSM6DSV16X_CONFIG_I3C(inst, prefix);

View File

@ -17,6 +17,7 @@
#include <zephyr/sys/util.h> #include <zephyr/sys/util.h>
#include <stmemsc.h> #include <stmemsc.h>
#include "lsm6dsv16x_reg.h" #include "lsm6dsv16x_reg.h"
#include <zephyr/rtio/regmap.h>
#define DT_DRV_COMPAT_LSM6DSV16X st_lsm6dsv16x #define DT_DRV_COMPAT_LSM6DSV16X st_lsm6dsv16x
#define DT_DRV_COMPAT_LSM6DSV32X st_lsm6dsv32x #define DT_DRV_COMPAT_LSM6DSV32X st_lsm6dsv32x
@ -171,9 +172,9 @@ struct lsm6dsv16x_data {
uint16_t accel_batch_odr : 4; uint16_t accel_batch_odr : 4;
uint16_t gyro_batch_odr : 4; uint16_t gyro_batch_odr : 4;
uint16_t temp_batch_odr : 2; 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 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_x_udps;
int32_t gbias_y_udps; int32_t gbias_y_udps;
int32_t gbias_z_udps; int32_t gbias_z_udps;
@ -206,13 +207,9 @@ struct lsm6dsv16x_data {
}; };
#ifdef CONFIG_LSM6DSV16X_STREAM #ifdef CONFIG_LSM6DSV16X_STREAM
#define BUS_I2C 0 static inline uint8_t lsm6dsv16x_bus_reg(rtio_bus_type bus, uint8_t addr)
#define BUS_SPI 1
#define BUS_I3C 2
static inline uint8_t lsm6dsv16x_bus_reg(struct lsm6dsv16x_data *data, uint8_t x)
{ {
return (data->bus_type == BUS_SPI) ? x | 0x80 : x; return (rtio_is_spi(bus)) ? addr | 0x80 : addr;
} }
#define LSM6DSV16X_FIFO_ITEM_LEN 7 #define LSM6DSV16X_FIFO_ITEM_LEN 7

View File

@ -16,48 +16,6 @@
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(LSM6DSV16X_RTIO); 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, &reg_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) static void lsm6dsv16x_config_drdy(const struct device *dev, struct trigger_config trig_cfg)
{ {
const struct lsm6dsv16x_config *config = dev->config; 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); read_buf = buf + sizeof(hdr);
buf_avail = buf_len - 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 * Prepare rtio enabled bus to read all fifo_count entries from
* LSM6DSV16X_FIFO_DATA_OUT_TAG. Then lsm6dsv16x_complete_op_cb * 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_fifo_out_raw_get(&dev_ctx, &f_data);
* } * }
*/ */
lsm6dsv16x_rtio_rw_transaction(dev, LSM6DSV16X_FIFO_DATA_OUT_TAG, rtio_read_regs_async(lsm6dsv16x->rtio_ctx, lsm6dsv16x->iodev, lsm6dsv16x->bus_type,
read_buf, buf_avail, lsm6dsv16x_complete_op_cb); &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)); memcpy(buf, &hdr, sizeof(hdr));
read_buf = (uint8_t *)&((struct lsm6dsv16x_rtio_data *)buf)->acc[0]; 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 * Prepare rtio enabled bus to read LSM6DSV16X_OUTX_L_A register
* where accelerometer data is available. * 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_acceleration_raw_get(&dev_ctx, accel_raw);
*/ */
lsm6dsv16x_rtio_rw_transaction(dev, LSM6DSV16X_OUTX_L_A, rtio_read_regs_async(lsm6dsv16x->rtio_ctx, lsm6dsv16x->iodev, lsm6dsv16x->bus_type,
read_buf, 6, lsm6dsv16x_complete_op_cb); &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); struct rtio_sqe *check_fifo_status_reg = rtio_sqe_acquire(rtio);
rtio_sqe_prep_callback_no_cqe(check_fifo_status_reg, 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); rtio_submit(rtio, 0);
} else { } else {
#endif #endif
lsm6dsv16x->fifo_status[0] = lsm6dsv16x->fifo_status[1] = 0; 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 * Prepare rtio enabled bus to read LSM6DSV16X_FIFO_STATUS1 and
* LSM6DSV16X_FIFO_STATUS2 registers where FIFO threshold condition 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_fifo_status_get(&dev_ctx, &fifo_status);
*/ */
lsm6dsv16x_rtio_rw_transaction(dev, LSM6DSV16X_FIFO_STATUS1, rtio_read_regs_async(lsm6dsv16x->rtio_ctx, lsm6dsv16x->iodev,
lsm6dsv16x->fifo_status, 2, lsm6dsv16x_read_fifo_cb); lsm6dsv16x->bus_type, &fifo_regs,
lsm6dsv16x->streaming_sqe, dev,
lsm6dsv16x_read_fifo_cb);
#if LSM6DSVXXX_ANY_INST_ON_BUS_STATUS_OKAY(i3c) #if LSM6DSVXXX_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
} }
#endif #endif
@ -737,6 +739,19 @@ void lsm6dsv16x_stream_irq_handler(const struct device *dev)
if (lsm6dsv16x->trig_cfg.int_drdy) { if (lsm6dsv16x->trig_cfg.int_drdy) {
lsm6dsv16x->status = 0; 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 * Prepare rtio enabled bus to read LSM6DSV16X_STATUS_REG register
* where accelerometer and gyroscope data ready status is available. * 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_flag_data_ready_get(&dev_ctx, &drdy);
*/ */
lsm6dsv16x_rtio_rw_transaction(dev, LSM6DSV16X_STATUS_REG, rtio_read_regs_async(lsm6dsv16x->rtio_ctx, lsm6dsv16x->iodev, lsm6dsv16x->bus_type,
&lsm6dsv16x->status, 1, lsm6dsv16x_read_status_cb); &fifo_regs, lsm6dsv16x->streaming_sqe, dev,
lsm6dsv16x_read_status_cb);
} }
} }

View File

@ -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 <zephyr/rtio/rtio.h>
#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,
* &regs,
* 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,
&regs->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_ */