diff --git a/doc/services/rtio/index.rst b/doc/services/rtio/index.rst index 5493b7dd59d..99d99c54430 100644 --- a/doc/services/rtio/index.rst +++ b/doc/services/rtio/index.rst @@ -212,6 +212,20 @@ There is a small cost to each RTIO context and iodev. This cost could be weighed against using a thread for each concurrent I/O operation or custom queues and threads per peripheral. RTIO is much lower cost than that. +Supported Buses +*************** + +To check if your bus supports RTIO natively, you can check the driver API implementation, if the +driver implements the ``iodev_submit`` function of the bus API, then RTIO is supported. If the +driver doesn't support the RTIO APIs, it will set the submit function to +``i2c_iodev_submit_fallback``. + +I2C buses have a default implementation which allows apps to leverage the RTIO work queue while +vendors implement the submit function. With this queue, any I2C bus driver that does not implement +the ``iodev_submit`` function will defer to a work item which will perform a blocking I2C +transaction. To change the pool size, set a different value to +:kconfig:option`CONFIG_RTIO_WORKQ_POOL_ITEMS`. + API Reference ************* diff --git a/drivers/i2c/CMakeLists.txt b/drivers/i2c/CMakeLists.txt index b46d93307ee..cf7155ecdd8 100644 --- a/drivers/i2c/CMakeLists.txt +++ b/drivers/i2c/CMakeLists.txt @@ -6,7 +6,10 @@ zephyr_library() zephyr_library_sources(i2c_common.c) -zephyr_library_sources_ifdef(CONFIG_I2C_RTIO i2c_rtio.c) +zephyr_library_sources_ifdef(CONFIG_I2C_RTIO + i2c_rtio.c + i2c_rtio_default.c +) zephyr_library_sources_ifdef(CONFIG_I2C_SHELL i2c_shell.c) zephyr_library_sources_ifdef(CONFIG_I2C_BITBANG i2c_bitbang.c) zephyr_library_sources_ifdef(CONFIG_I2C_TELINK_B91 i2c_b91.c) diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index f005333bcb7..9c90a500ba9 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -54,16 +54,11 @@ config I2C_CALLBACK help API and implementations of i2c_transfer_cb. -config HAS_I2C_RTIO - bool - help - This option must be selected by I2C controller drivers that optionally implement the RTIO - interface. - config I2C_RTIO bool "I2C RTIO API" - depends on HAS_I2C_RTIO + select EXPERIMENTAL select RTIO + select RTIO_WORKQ help API and implementations of I2C for RTIO @@ -177,7 +172,6 @@ config I2C_MCUX_LPI2C default y depends on DT_HAS_NXP_IMX_LPI2C_ENABLED depends on CLOCK_CONTROL - select HAS_I2C_RTIO select PINCTRL help Enable the mcux LPI2C driver. diff --git a/drivers/i2c/Kconfig.nrfx b/drivers/i2c/Kconfig.nrfx index 3c0596fc5b9..6898d6e42a9 100644 --- a/drivers/i2c/Kconfig.nrfx +++ b/drivers/i2c/Kconfig.nrfx @@ -17,7 +17,6 @@ if I2C_NRFX config I2C_NRFX_TWI def_bool y depends on DT_HAS_NORDIC_NRF_TWI_ENABLED - select HAS_I2C_RTIO select NRFX_TWI0 if HAS_HW_NRF_TWI0 select NRFX_TWI1 if HAS_HW_NRF_TWI1 diff --git a/drivers/i2c/Kconfig.sam_twihs b/drivers/i2c/Kconfig.sam_twihs index ee3a2948752..5f1daa6f4c4 100644 --- a/drivers/i2c/Kconfig.sam_twihs +++ b/drivers/i2c/Kconfig.sam_twihs @@ -7,6 +7,5 @@ config I2C_SAM_TWIHS bool "Atmel SAM (TWIHS) I2C driver" default y depends on DT_HAS_ATMEL_SAM_I2C_TWIHS_ENABLED - select HAS_I2C_RTIO help Enable Atmel SAM MCU Family (TWIHS) I2C bus driver. diff --git a/drivers/i2c/gpio_i2c_switch.c b/drivers/i2c/gpio_i2c_switch.c index d5a125a531b..5276beb084d 100644 --- a/drivers/i2c/gpio_i2c_switch.c +++ b/drivers/i2c/gpio_i2c_switch.c @@ -67,6 +67,9 @@ static int gpio_i2c_switch_transfer(const struct device *dev, struct i2c_msg *ms static const struct i2c_driver_api gpio_i2c_switch_api_funcs = { .configure = gpio_i2c_switch_configure, .transfer = gpio_i2c_switch_transfer, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; static int gpio_i2c_switch_init(const struct device *dev) diff --git a/drivers/i2c/i2c_ambiq.c b/drivers/i2c/i2c_ambiq.c index a8a06d09924..fd67bacdf8b 100644 --- a/drivers/i2c/i2c_ambiq.c +++ b/drivers/i2c/i2c_ambiq.c @@ -275,6 +275,9 @@ end: static const struct i2c_driver_api i2c_ambiq_driver_api = { .configure = i2c_ambiq_configure, .transfer = i2c_ambiq_transfer, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; #ifdef CONFIG_PM_DEVICE diff --git a/drivers/i2c/i2c_andes_atciic100.c b/drivers/i2c/i2c_andes_atciic100.c index 918048ee985..3f1b4b9c3d2 100644 --- a/drivers/i2c/i2c_andes_atciic100.c +++ b/drivers/i2c/i2c_andes_atciic100.c @@ -733,13 +733,14 @@ static void i2c_atciic100_irq_handler(void *arg) } static const struct i2c_driver_api i2c_atciic100_driver = { - .configure = (i2c_api_configure_t)i2c_atciic100_configure, - .transfer = (i2c_api_full_io_t)i2c_atciic100_transfer, + .configure = (i2c_api_configure_t)i2c_atciic100_configure, + .transfer = (i2c_api_full_io_t)i2c_atciic100_transfer, #if defined(CONFIG_I2C_TARGET) - .target_register = - (i2c_api_target_register_t)i2c_atciic100_target_register, - .target_unregister = - (i2c_api_target_unregister_t)i2c_atciic100_target_unregister + .target_register = (i2c_api_target_register_t)i2c_atciic100_target_register, + .target_unregister = (i2c_api_target_unregister_t)i2c_atciic100_target_unregister, +#endif +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, #endif }; diff --git a/drivers/i2c/i2c_b91.c b/drivers/i2c/i2c_b91.c index d140c534ced..f7b25f54fd1 100644 --- a/drivers/i2c/i2c_b91.c +++ b/drivers/i2c/i2c_b91.c @@ -150,6 +150,9 @@ static int i2c_b91_init(const struct device *dev) static const struct i2c_driver_api i2c_b91_api = { .configure = i2c_b91_configure, .transfer = i2c_b91_transfer, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) <= 1, diff --git a/drivers/i2c/i2c_bcm_iproc.c b/drivers/i2c/i2c_bcm_iproc.c index fbc5dc47583..18f6afb0ae6 100644 --- a/drivers/i2c/i2c_bcm_iproc.c +++ b/drivers/i2c/i2c_bcm_iproc.c @@ -923,6 +923,9 @@ static const struct i2c_driver_api iproc_i2c_driver_api = { .target_register = iproc_i2c_target_register, .target_unregister = iproc_i2c_target_unregister, #endif +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; #define IPROC_I2C_DEVICE_INIT(n) \ diff --git a/drivers/i2c/i2c_cc13xx_cc26xx.c b/drivers/i2c/i2c_cc13xx_cc26xx.c index 5d8de8641f5..d06ffe3c5cb 100644 --- a/drivers/i2c/i2c_cc13xx_cc26xx.c +++ b/drivers/i2c/i2c_cc13xx_cc26xx.c @@ -421,7 +421,10 @@ static int i2c_cc13xx_cc26xx_init(const struct device *dev) static const struct i2c_driver_api i2c_cc13xx_cc26xx_driver_api = { .configure = i2c_cc13xx_cc26xx_configure, - .transfer = i2c_cc13xx_cc26xx_transfer + .transfer = i2c_cc13xx_cc26xx_transfer, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; PINCTRL_DT_INST_DEFINE(0); diff --git a/drivers/i2c/i2c_cc32xx.c b/drivers/i2c/i2c_cc32xx.c index b7caf5393fe..57fee761799 100644 --- a/drivers/i2c/i2c_cc32xx.c +++ b/drivers/i2c/i2c_cc32xx.c @@ -382,9 +382,11 @@ static int i2c_cc32xx_init(const struct device *dev) static const struct i2c_driver_api i2c_cc32xx_driver_api = { .configure = i2c_cc32xx_configure, .transfer = i2c_cc32xx_transfer, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; - PINCTRL_DT_INST_DEFINE(0); static const struct i2c_cc32xx_config i2c_cc32xx_config = { diff --git a/drivers/i2c/i2c_dw.c b/drivers/i2c/i2c_dw.c index 9102360ca55..1981df2eee1 100644 --- a/drivers/i2c/i2c_dw.c +++ b/drivers/i2c/i2c_dw.c @@ -1023,6 +1023,9 @@ static const struct i2c_driver_api funcs = { .target_register = i2c_dw_slave_register, .target_unregister = i2c_dw_slave_unregister, #endif /* CONFIG_I2C_TARGET */ +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; static int i2c_dw_initialize(const struct device *dev) diff --git a/drivers/i2c/i2c_emul.c b/drivers/i2c/i2c_emul.c index 0ec27f99222..5554ce81b5c 100644 --- a/drivers/i2c/i2c_emul.c +++ b/drivers/i2c/i2c_emul.c @@ -300,6 +300,9 @@ static const struct i2c_driver_api i2c_emul_api = { .target_register = i2c_emul_target_register, .target_unregister = i2c_emul_target_unregister, #endif +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; #define EMUL_LINK_AND_COMMA(node_id) \ diff --git a/drivers/i2c/i2c_ene_kb1200.c b/drivers/i2c/i2c_ene_kb1200.c index 3a24af2e672..021c123fe98 100644 --- a/drivers/i2c/i2c_ene_kb1200.c +++ b/drivers/i2c/i2c_ene_kb1200.c @@ -297,6 +297,9 @@ static const struct i2c_driver_api i2c_kb1200_api = { .configure = i2c_kb1200_configure, .get_config = i2c_kb1200_get_config, .transfer = i2c_kb1200_transfer, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; #define KB1200_FSMBM_DEV(inst) DEVICE_DT_INST_GET(inst), diff --git a/drivers/i2c/i2c_esp32.c b/drivers/i2c/i2c_esp32.c index d15dac1142a..67569817472 100644 --- a/drivers/i2c/i2c_esp32.c +++ b/drivers/i2c/i2c_esp32.c @@ -730,7 +730,10 @@ static const struct i2c_driver_api i2c_esp32_driver_api = { .configure = i2c_esp32_configure, .get_config = i2c_esp32_get_config, .transfer = i2c_esp32_transfer, - .recover_bus = i2c_esp32_recover + .recover_bus = i2c_esp32_recover, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; static int IRAM_ATTR i2c_esp32_init(const struct device *dev) diff --git a/drivers/i2c/i2c_gd32.c b/drivers/i2c/i2c_gd32.c index 15b34956d5a..9b6d6f5e4e6 100644 --- a/drivers/i2c/i2c_gd32.c +++ b/drivers/i2c/i2c_gd32.c @@ -644,6 +644,9 @@ error: static const struct i2c_driver_api i2c_gd32_driver_api = { .configure = i2c_gd32_configure, .transfer = i2c_gd32_transfer, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; static int i2c_gd32_init(const struct device *dev) diff --git a/drivers/i2c/i2c_gecko.c b/drivers/i2c/i2c_gecko.c index cf44104104a..838e42d0388 100644 --- a/drivers/i2c/i2c_gecko.c +++ b/drivers/i2c/i2c_gecko.c @@ -208,6 +208,9 @@ static const struct i2c_driver_api i2c_gecko_driver_api = { .target_register = i2c_gecko_target_register, .target_unregister = i2c_gecko_target_unregister, #endif +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; #if defined(CONFIG_I2C_TARGET) diff --git a/drivers/i2c/i2c_gpio.c b/drivers/i2c/i2c_gpio.c index 80c20f38ba6..80a1a221490 100644 --- a/drivers/i2c/i2c_gpio.c +++ b/drivers/i2c/i2c_gpio.c @@ -145,6 +145,9 @@ static const struct i2c_driver_api api = { .get_config = i2c_gpio_get_config, .transfer = i2c_gpio_transfer, .recover_bus = i2c_gpio_recover_bus, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; static int i2c_gpio_init(const struct device *dev) diff --git a/drivers/i2c/i2c_ifx_cat1.c b/drivers/i2c/i2c_ifx_cat1.c index 6533dda9c8d..90bf9c1e68b 100644 --- a/drivers/i2c/i2c_ifx_cat1.c +++ b/drivers/i2c/i2c_ifx_cat1.c @@ -497,7 +497,11 @@ static const struct i2c_driver_api i2c_cat1_driver_api = { .transfer = ifx_cat1_i2c_transfer, .get_config = ifx_cat1_i2c_get_config, .target_register = ifx_cat1_i2c_target_register, - .target_unregister = ifx_cat1_i2c_target_unregister}; + .target_unregister = ifx_cat1_i2c_target_unregister, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif +}; /* Macros for I2C instance declaration */ #define INFINEON_CAT1_I2C_INIT(n) \ diff --git a/drivers/i2c/i2c_ifx_xmc4.c b/drivers/i2c/i2c_ifx_xmc4.c index 82994eae077..253445809c3 100644 --- a/drivers/i2c/i2c_ifx_xmc4.c +++ b/drivers/i2c/i2c_ifx_xmc4.c @@ -430,7 +430,11 @@ static const struct i2c_driver_api i2c_xmc4_driver_api = { .transfer = ifx_xmc4_i2c_transfer, .get_config = ifx_xmc4_i2c_get_config, .target_register = ifx_xmc4_i2c_target_register, - .target_unregister = ifx_xmc4_i2c_target_unregister}; + .target_unregister = ifx_xmc4_i2c_target_unregister, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif +}; /* Macros for I2C instance declaration */ #define XMC4_IRQ_HANDLER_INIT(index) \ diff --git a/drivers/i2c/i2c_imx.c b/drivers/i2c/i2c_imx.c index fd6ca284201..eba04101693 100644 --- a/drivers/i2c/i2c_imx.c +++ b/drivers/i2c/i2c_imx.c @@ -363,6 +363,9 @@ static int i2c_imx_init(const struct device *dev) static const struct i2c_driver_api i2c_imx_driver_api = { .configure = i2c_imx_configure, .transfer = i2c_imx_transfer, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; #define I2C_IMX_INIT(n) \ diff --git a/drivers/i2c/i2c_ite_enhance.c b/drivers/i2c/i2c_ite_enhance.c index eae90489c7e..0b844e75254 100644 --- a/drivers/i2c/i2c_ite_enhance.c +++ b/drivers/i2c/i2c_ite_enhance.c @@ -1461,6 +1461,9 @@ static const struct i2c_driver_api i2c_enhance_driver_api = { .target_register = i2c_enhance_target_register, .target_unregister = i2c_enhance_target_unregister, #endif +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; #ifdef CONFIG_I2C_TARGET diff --git a/drivers/i2c/i2c_ite_it8xxx2.c b/drivers/i2c/i2c_ite_it8xxx2.c index b639d4e5377..c4579a4f5e8 100644 --- a/drivers/i2c/i2c_ite_it8xxx2.c +++ b/drivers/i2c/i2c_ite_it8xxx2.c @@ -1255,6 +1255,9 @@ static const struct i2c_driver_api i2c_it8xxx2_driver_api = { .get_config = i2c_it8xxx2_get_config, .transfer = i2c_it8xxx2_transfer, .recover_bus = i2c_it8xxx2_recover_bus, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; #ifdef CONFIG_I2C_IT8XXX2_FIFO_MODE diff --git a/drivers/i2c/i2c_litex.c b/drivers/i2c/i2c_litex.c index b237790e91c..38e0cfa8cfd 100644 --- a/drivers/i2c/i2c_litex.c +++ b/drivers/i2c/i2c_litex.c @@ -141,6 +141,9 @@ static const struct i2c_driver_api i2c_litex_driver_api = { .get_config = i2c_litex_get_config, .transfer = i2c_litex_transfer, .recover_bus = i2c_litex_recover_bus, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; /* Device Instantiation */ diff --git a/drivers/i2c/i2c_ll_stm32.c b/drivers/i2c/i2c_ll_stm32.c index 56069989088..2e5b498fac6 100644 --- a/drivers/i2c/i2c_ll_stm32.c +++ b/drivers/i2c/i2c_ll_stm32.c @@ -308,7 +308,6 @@ restore: } #endif /* CONFIG_I2C_STM32_BUS_RECOVERY */ - static const struct i2c_driver_api api_funcs = { .configure = i2c_stm32_runtime_configure, .transfer = i2c_stm32_transfer, @@ -320,6 +319,9 @@ static const struct i2c_driver_api api_funcs = { .target_register = i2c_stm32_target_register, .target_unregister = i2c_stm32_target_unregister, #endif +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; #ifdef CONFIG_PM_DEVICE diff --git a/drivers/i2c/i2c_lpc11u6x.c b/drivers/i2c/i2c_lpc11u6x.c index a7e7c8f61b8..2a32822be5c 100644 --- a/drivers/i2c/i2c_lpc11u6x.c +++ b/drivers/i2c/i2c_lpc11u6x.c @@ -345,6 +345,9 @@ static const struct i2c_driver_api i2c_api = { .transfer = lpc11u6x_i2c_transfer, .target_register = lpc11u6x_i2c_slave_register, .target_unregister = lpc11u6x_i2c_slave_unregister, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; #define LPC11U6X_I2C_INIT(idx) \ diff --git a/drivers/i2c/i2c_max32.c b/drivers/i2c/i2c_max32.c index 69901c31933..e0861211432 100644 --- a/drivers/i2c/i2c_max32.c +++ b/drivers/i2c/i2c_max32.c @@ -838,6 +838,9 @@ static const struct i2c_driver_api api = { #ifdef CONFIG_I2C_TARGET .target_register = api_target_register, .target_unregister = api_target_unregister, +#endif +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, #endif .recover_bus = api_recover_bus, }; diff --git a/drivers/i2c/i2c_mchp_mss.c b/drivers/i2c/i2c_mchp_mss.c index 49b0da2a719..721192e281a 100644 --- a/drivers/i2c/i2c_mchp_mss.c +++ b/drivers/i2c/i2c_mchp_mss.c @@ -235,6 +235,9 @@ static int mss_i2c_transfer(const struct device *dev, struct i2c_msg *msgs, uint static const struct i2c_driver_api mss_i2c_driver_api = { .configure = mss_i2c_configure, .transfer = mss_i2c_transfer, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; static void mss_i2c_reset(const struct device *dev) diff --git a/drivers/i2c/i2c_mchp_xec.c b/drivers/i2c/i2c_mchp_xec.c index 231560d443d..6f48e9404b1 100644 --- a/drivers/i2c/i2c_mchp_xec.c +++ b/drivers/i2c/i2c_mchp_xec.c @@ -836,6 +836,9 @@ static const struct i2c_driver_api i2c_xec_driver_api = { .target_register = i2c_xec_target_register, .target_unregister = i2c_xec_target_unregister, #endif +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; static int i2c_xec_init(const struct device *dev) diff --git a/drivers/i2c/i2c_mchp_xec_v2.c b/drivers/i2c/i2c_mchp_xec_v2.c index 0fd9292933b..c0ab170ba03 100644 --- a/drivers/i2c/i2c_mchp_xec_v2.c +++ b/drivers/i2c/i2c_mchp_xec_v2.c @@ -1040,6 +1040,9 @@ static const struct i2c_driver_api i2c_xec_driver_api = { .target_register = i2c_xec_target_register, .target_unregister = i2c_xec_target_unregister, #endif +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; static int i2c_xec_init(const struct device *dev) diff --git a/drivers/i2c/i2c_mcux.c b/drivers/i2c/i2c_mcux.c index 2a927018a5f..aaf6b0116a6 100644 --- a/drivers/i2c/i2c_mcux.c +++ b/drivers/i2c/i2c_mcux.c @@ -348,6 +348,9 @@ static const struct i2c_driver_api i2c_mcux_driver_api = { #ifdef CONFIG_I2C_CALLBACK .transfer_cb = i2c_mcux_transfer_cb, #endif +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; #define I2C_DEVICE_INIT_MCUX(n) \ diff --git a/drivers/i2c/i2c_mcux_flexcomm.c b/drivers/i2c/i2c_mcux_flexcomm.c index f75c65650ec..158deacc6de 100644 --- a/drivers/i2c/i2c_mcux_flexcomm.c +++ b/drivers/i2c/i2c_mcux_flexcomm.c @@ -526,6 +526,9 @@ static const struct i2c_driver_api mcux_flexcomm_driver_api = { .target_register = mcux_flexcomm_target_register, .target_unregister = mcux_flexcomm_target_unregister, #endif +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; #define I2C_MCUX_FLEXCOMM_DEVICE(id) \ diff --git a/drivers/i2c/i2c_nios2.c b/drivers/i2c/i2c_nios2.c index 1300b2d95b6..bbd1cf2223a 100644 --- a/drivers/i2c/i2c_nios2.c +++ b/drivers/i2c/i2c_nios2.c @@ -155,6 +155,9 @@ static int i2c_nios2_init(const struct device *dev); static const struct i2c_driver_api i2c_nios2_driver_api = { .configure = i2c_nios2_configure, .transfer = i2c_nios2_transfer, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; static struct i2c_nios2_data i2c_nios2_dev_data = { diff --git a/drivers/i2c/i2c_npcx_port.c b/drivers/i2c/i2c_npcx_port.c index b43383c18d2..17638909561 100644 --- a/drivers/i2c/i2c_npcx_port.c +++ b/drivers/i2c/i2c_npcx_port.c @@ -209,6 +209,9 @@ static const struct i2c_driver_api i2c_port_npcx_driver_api = { .target_register = i2c_npcx_target_register, .target_unregister = i2c_npcx_target_unregister, #endif +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; /* I2C port init macro functions */ diff --git a/drivers/i2c/i2c_nrfx_twim.c b/drivers/i2c/i2c_nrfx_twim.c index 8cc849b03c9..4d1ff0f4640 100644 --- a/drivers/i2c/i2c_nrfx_twim.c +++ b/drivers/i2c/i2c_nrfx_twim.c @@ -289,8 +289,11 @@ static int i2c_nrfx_twim_recover_bus(const struct device *dev) } static const struct i2c_driver_api i2c_nrfx_twim_driver_api = { - .configure = i2c_nrfx_twim_configure, - .transfer = i2c_nrfx_twim_transfer, + .configure = i2c_nrfx_twim_configure, + .transfer = i2c_nrfx_twim_transfer, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif .recover_bus = i2c_nrfx_twim_recover_bus, }; diff --git a/drivers/i2c/i2c_numaker.c b/drivers/i2c/i2c_numaker.c index 0c856015660..9441db55ca0 100644 --- a/drivers/i2c/i2c_numaker.c +++ b/drivers/i2c/i2c_numaker.c @@ -739,6 +739,9 @@ static const struct i2c_driver_api i2c_numaker_driver_api = { #ifdef CONFIG_I2C_TARGET .target_register = i2c_numaker_slave_register, .target_unregister = i2c_numaker_slave_unregister, +#endif +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, #endif .recover_bus = i2c_numaker_recover_bus, }; diff --git a/drivers/i2c/i2c_rcar.c b/drivers/i2c/i2c_rcar.c index d6ac6678853..495579bb1e4 100644 --- a/drivers/i2c/i2c_rcar.c +++ b/drivers/i2c/i2c_rcar.c @@ -347,6 +347,9 @@ static int i2c_rcar_init(const struct device *dev) static const struct i2c_driver_api i2c_rcar_driver_api = { .configure = i2c_rcar_configure, .transfer = i2c_rcar_transfer, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; /* Device Instantiation */ diff --git a/drivers/i2c/i2c_rtio_default.c b/drivers/i2c/i2c_rtio_default.c new file mode 100644 index 00000000000..c7f94ac9bd2 --- /dev/null +++ b/drivers/i2c/i2c_rtio_default.c @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2024 Google LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include +LOG_MODULE_DECLARE(i2c_rtio, CONFIG_I2C_LOG_LEVEL); + +static int i2c_iodev_submit_rx(struct rtio_iodev_sqe *iodev_sqe, struct i2c_msg msgs[2], + uint8_t *num_msgs) +{ + __ASSERT_NO_MSG(iodev_sqe->sqe.op == RTIO_OP_RX); + + msgs[0].buf = iodev_sqe->sqe.rx.buf; + msgs[0].len = iodev_sqe->sqe.rx.buf_len; + msgs[0].flags = + ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_STOP) ? I2C_MSG_STOP : 0) | + ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_RESTART) ? I2C_MSG_RESTART : 0) | + ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_10_BITS) ? I2C_MSG_ADDR_10_BITS : 0) | + I2C_MSG_READ; + *num_msgs = 1; + return 0; +} + +static int i2c_iodev_submit_tx(struct rtio_iodev_sqe *iodev_sqe, struct i2c_msg msgs[2], + uint8_t *num_msgs) +{ + __ASSERT_NO_MSG(iodev_sqe->sqe.op == RTIO_OP_TX); + + msgs[0].buf = (uint8_t *)iodev_sqe->sqe.tx.buf; + msgs[0].len = iodev_sqe->sqe.tx.buf_len; + msgs[0].flags = + ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_STOP) ? I2C_MSG_STOP : 0) | + ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_RESTART) ? I2C_MSG_RESTART : 0) | + ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_10_BITS) ? I2C_MSG_ADDR_10_BITS : 0) | + I2C_MSG_WRITE; + *num_msgs = 1; + return 0; +} + +static int i2c_iodev_submit_tiny_tx(struct rtio_iodev_sqe *iodev_sqe, struct i2c_msg msgs[2], + uint8_t *num_msgs) +{ + __ASSERT_NO_MSG(iodev_sqe->sqe.op == RTIO_OP_TINY_TX); + + msgs[0].buf = (uint8_t *)iodev_sqe->sqe.tiny_tx.buf; + msgs[0].len = iodev_sqe->sqe.tiny_tx.buf_len; + msgs[0].flags = + ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_STOP) ? I2C_MSG_STOP : 0) | + ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_RESTART) ? I2C_MSG_RESTART : 0) | + ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_10_BITS) ? I2C_MSG_ADDR_10_BITS : 0) | + I2C_MSG_WRITE; + *num_msgs = 1; + return 0; +} + +static int i2c_iodev_submit_txrx(struct rtio_iodev_sqe *iodev_sqe, struct i2c_msg msgs[2], + uint8_t *num_msgs) +{ + __ASSERT_NO_MSG(iodev_sqe->sqe.op == RTIO_OP_TXRX); + + msgs[0].buf = (uint8_t *)iodev_sqe->sqe.txrx.tx_buf; + msgs[0].len = iodev_sqe->sqe.txrx.buf_len; + msgs[0].flags = + ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_10_BITS) ? I2C_MSG_ADDR_10_BITS : 0) | + I2C_MSG_WRITE; + msgs[1].buf = iodev_sqe->sqe.txrx.rx_buf; + msgs[1].len = iodev_sqe->sqe.txrx.buf_len; + msgs[1].flags = + ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_STOP) ? I2C_MSG_STOP : 0) | + ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_RESTART) ? I2C_MSG_RESTART : 0) | + ((iodev_sqe->sqe.iodev_flags & RTIO_IODEV_I2C_10_BITS) ? I2C_MSG_ADDR_10_BITS : 0) | + I2C_MSG_READ; + *num_msgs = 2; + return 0; +} + +void i2c_iodev_submit_work_handler(struct rtio_iodev_sqe *iodev_sqe) +{ + const struct i2c_dt_spec *dt_spec = (const struct i2c_dt_spec *)iodev_sqe->sqe.iodev->data; + const struct device *dev = dt_spec->bus; + + LOG_DBG("Sync RTIO work item for: %p", (void *)iodev_sqe); + + struct rtio_iodev_sqe *transaction_current = iodev_sqe; + struct i2c_msg msgs[2]; + uint8_t num_msgs; + int rc = 0; + + do { + /* Convert the iodev_sqe back to an i2c_msg */ + switch (transaction_current->sqe.op) { + case RTIO_OP_RX: + rc = i2c_iodev_submit_rx(transaction_current, msgs, &num_msgs); + break; + case RTIO_OP_TX: + rc = i2c_iodev_submit_tx(transaction_current, msgs, &num_msgs); + break; + case RTIO_OP_TINY_TX: + rc = i2c_iodev_submit_tiny_tx(transaction_current, msgs, &num_msgs); + break; + case RTIO_OP_TXRX: + rc = i2c_iodev_submit_txrx(transaction_current, msgs, &num_msgs); + break; + default: + LOG_ERR("Invalid op code %d for submission %p", transaction_current->sqe.op, + (void *)&transaction_current->sqe); + rc = -EIO; + break; + } + + if (rc == 0) { + __ASSERT_NO_MSG(num_msgs > 0); + + rc = i2c_transfer(dev, msgs, num_msgs, dt_spec->addr); + transaction_current = rtio_txn_next(transaction_current); + } + } while (rc == 0 && transaction_current != NULL); + + if (rc != 0) { + rtio_iodev_sqe_err(iodev_sqe, rc); + } else { + rtio_iodev_sqe_ok(iodev_sqe, 0); + } +} + +void i2c_iodev_submit_fallback(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) +{ + LOG_DBG("Executing fallback for dev: %p, sqe: %p", (void *)dev, (void *)iodev_sqe); + + struct rtio_work_req *req = rtio_work_req_alloc(); + + if (req == NULL) { + rtio_iodev_sqe_err(iodev_sqe, -ENOMEM); + return; + } + + rtio_work_req_submit(req, iodev_sqe, i2c_iodev_submit_work_handler); +} diff --git a/drivers/i2c/i2c_rv32m1_lpi2c.c b/drivers/i2c/i2c_rv32m1_lpi2c.c index ef64f1ac3d3..a7bffef4d4b 100644 --- a/drivers/i2c/i2c_rv32m1_lpi2c.c +++ b/drivers/i2c/i2c_rv32m1_lpi2c.c @@ -257,7 +257,10 @@ static int rv32m1_lpi2c_init(const struct device *dev) static const struct i2c_driver_api rv32m1_lpi2c_driver_api = { .configure = rv32m1_lpi2c_configure, - .transfer = rv32m1_lpi2c_transfer, + .transfer = rv32m1_lpi2c_transfer, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; #define RV32M1_LPI2C_DEVICE(id) \ diff --git a/drivers/i2c/i2c_sam0.c b/drivers/i2c/i2c_sam0.c index e1a5b432916..9c9c5350cf3 100644 --- a/drivers/i2c/i2c_sam0.c +++ b/drivers/i2c/i2c_sam0.c @@ -761,10 +761,12 @@ static int i2c_sam0_initialize(const struct device *dev) return 0; } - static const struct i2c_driver_api i2c_sam0_driver_api = { .configure = i2c_sam0_configure, .transfer = i2c_sam0_transfer, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; #ifdef CONFIG_I2C_SAM0_DMA_DRIVEN diff --git a/drivers/i2c/i2c_sam4l_twim.c b/drivers/i2c/i2c_sam4l_twim.c index 372d9bd2418..3f8551267f7 100644 --- a/drivers/i2c/i2c_sam4l_twim.c +++ b/drivers/i2c/i2c_sam4l_twim.c @@ -591,6 +591,9 @@ static int i2c_sam_twim_initialize(const struct device *dev) static const struct i2c_driver_api i2c_sam_twim_driver_api = { .configure = i2c_sam_twim_configure, .transfer = i2c_sam_twim_transfer, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; #define I2C_TWIM_SAM_SLEW_REGS(n) \ diff --git a/drivers/i2c/i2c_sam_twi.c b/drivers/i2c/i2c_sam_twi.c index 3a5bc730f62..ad6a3277859 100644 --- a/drivers/i2c/i2c_sam_twi.c +++ b/drivers/i2c/i2c_sam_twi.c @@ -353,6 +353,9 @@ static int i2c_sam_twi_initialize(const struct device *dev) static const struct i2c_driver_api i2c_sam_twi_driver_api = { .configure = i2c_sam_twi_configure, .transfer = i2c_sam_twi_transfer, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; #define I2C_TWI_SAM_INIT(n) \ diff --git a/drivers/i2c/i2c_sbcon.c b/drivers/i2c/i2c_sbcon.c index 9e077a81956..0212593ed3e 100644 --- a/drivers/i2c/i2c_sbcon.c +++ b/drivers/i2c/i2c_sbcon.c @@ -118,6 +118,9 @@ static const struct i2c_driver_api api = { .get_config = i2c_sbcon_get_config, .transfer = i2c_sbcon_transfer, .recover_bus = i2c_sbcon_recover_bus, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; static int i2c_sbcon_init(const struct device *dev) diff --git a/drivers/i2c/i2c_sc18im704.c b/drivers/i2c/i2c_sc18im704.c index 350568cd639..5bee2c546aa 100644 --- a/drivers/i2c/i2c_sc18im704.c +++ b/drivers/i2c/i2c_sc18im704.c @@ -324,6 +324,9 @@ static const struct i2c_driver_api i2c_sc18im_driver_api = { .configure = i2c_sc18im_configure, .get_config = i2c_sc18im_get_config, .transfer = i2c_sc18im_transfer, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; #define I2C_SC18IM_DEFINE(n) \ diff --git a/drivers/i2c/i2c_sedi.c b/drivers/i2c/i2c_sedi.c index 4802e0ba071..00ec5e06d4a 100644 --- a/drivers/i2c/i2c_sedi.c +++ b/drivers/i2c/i2c_sedi.c @@ -114,7 +114,10 @@ static int i2c_sedi_api_full_io(const struct device *dev, struct i2c_msg *msgs, static const struct i2c_driver_api i2c_sedi_apis = { .configure = i2c_sedi_api_configure, - .transfer = i2c_sedi_api_full_io + .transfer = i2c_sedi_api_full_io, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; #ifdef CONFIG_PM_DEVICE diff --git a/drivers/i2c/i2c_sifive.c b/drivers/i2c/i2c_sifive.c index ca2e4ae8b68..54ba5a779e4 100644 --- a/drivers/i2c/i2c_sifive.c +++ b/drivers/i2c/i2c_sifive.c @@ -316,10 +316,12 @@ static int i2c_sifive_init(const struct device *dev) return 0; } - static const struct i2c_driver_api i2c_sifive_api = { .configure = i2c_sifive_configure, .transfer = i2c_sifive_transfer, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; /* Device instantiation */ diff --git a/drivers/i2c/i2c_smartbond.c b/drivers/i2c/i2c_smartbond.c index 64c6e92b3d1..8b5891641b3 100644 --- a/drivers/i2c/i2c_smartbond.c +++ b/drivers/i2c/i2c_smartbond.c @@ -557,6 +557,9 @@ static const struct i2c_driver_api i2c_smartbond_driver_api = { #ifdef CONFIG_I2C_CALLBACK .transfer_cb = i2c_smartbond_transfer_cb, #endif +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; static int i2c_smartbond_resume(const struct device *dev) diff --git a/drivers/i2c/i2c_tca954x.c b/drivers/i2c/i2c_tca954x.c index 9a61d2bad17..5f2572dac31 100644 --- a/drivers/i2c/i2c_tca954x.c +++ b/drivers/i2c/i2c_tca954x.c @@ -154,6 +154,9 @@ static int tca954x_channel_init(const struct device *dev) static const struct i2c_driver_api tca954x_api_funcs = { .configure = tca954x_configure, .transfer = tca954x_transfer, +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; BUILD_ASSERT(CONFIG_I2C_TCA954X_CHANNEL_INIT_PRIO > CONFIG_I2C_TCA954X_ROOT_INIT_PRIO, diff --git a/drivers/i2c/i2c_xilinx_axi.c b/drivers/i2c/i2c_xilinx_axi.c index c940e8dab0a..715deaa6bb7 100644 --- a/drivers/i2c/i2c_xilinx_axi.c +++ b/drivers/i2c/i2c_xilinx_axi.c @@ -627,6 +627,9 @@ static const struct i2c_driver_api i2c_xilinx_axi_driver_api = { .target_register = i2c_xilinx_axi_target_register, .target_unregister = i2c_xilinx_axi_target_unregister, #endif +#ifdef CONFIG_I2C_RTIO + .iodev_submit = i2c_iodev_submit_fallback, +#endif }; #define I2C_XILINX_AXI_INIT(n, compat) \ diff --git a/include/zephyr/drivers/i2c.h b/include/zephyr/drivers/i2c.h index 7022f0f6efd..1455820ca88 100644 --- a/include/zephyr/drivers/i2c.h +++ b/include/zephyr/drivers/i2c.h @@ -1008,6 +1008,18 @@ static inline int i2c_transfer_signal(const struct device *dev, #if defined(CONFIG_I2C_RTIO) || defined(__DOXYGEN__) +/** + * @brief Fallback submit implementation + * + * This implementation will schedule a blocking I2C transaction on the bus via the RTIO work + * queue. It is only used if the I2C driver did not implement the iodev_submit function. + * + * @param dev Pointer to the device structure for an I2C controller driver. + * @param iodev_sqe Prepared submissions queue entry connected to an iodev + * defined by I2C_DT_IODEV_DEFINE. + */ +void i2c_iodev_submit_fallback(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe); + /** * @brief Submit request(s) to an I2C device with RTIO * @@ -1020,6 +1032,10 @@ static inline void i2c_iodev_submit(struct rtio_iodev_sqe *iodev_sqe) const struct device *dev = dt_spec->bus; const struct i2c_driver_api *api = (const struct i2c_driver_api *)dev->api; + if (api->iodev_submit == NULL) { + rtio_iodev_sqe_err(iodev_sqe, -ENOSYS); + return; + } api->iodev_submit(dt_spec->bus, iodev_sqe); } diff --git a/tests/drivers/i2c/i2c_ram/testcase.yaml b/tests/drivers/i2c/i2c_ram/testcase.yaml index 75bfbf0eb9a..503a932ef09 100644 --- a/tests/drivers/i2c/i2c_ram/testcase.yaml +++ b/tests/drivers/i2c/i2c_ram/testcase.yaml @@ -14,9 +14,10 @@ tests: - drivers - i2c drivers.i2c.ram.rtio: - filter: CONFIG_HAS_I2C_RTIO + filter: CONFIG_I2C_RTIO extra_configs: - CONFIG_I2C_RTIO=y + - CONFIG_ZTEST_THREAD_PRIORITY=2 drivers.i2c.ram.pm: filter: CONFIG_HAS_PM extra_configs: @@ -24,9 +25,10 @@ tests: - CONFIG_PM_DEVICE=y - CONFIG_PM_DEVICE_RUNTIME=y drivers.i2c.ram.pm.rtio: - filter: CONFIG_HAS_PM and CONFIG_HAS_I2C_RTIO + filter: CONFIG_HAS_PM and CONFIG_I2C_RTIO extra_configs: - CONFIG_PM=y - CONFIG_PM_DEVICE=y - CONFIG_PM_DEVICE_RUNTIME=y - CONFIG_I2C_RTIO=y + - CONFIG_ZTEST_THREAD_PRIORITY=2 diff --git a/tests/subsys/rtio/rtio_i2c/CMakeLists.txt b/tests/subsys/rtio/rtio_i2c/CMakeLists.txt new file mode 100644 index 00000000000..9a308ec0ca2 --- /dev/null +++ b/tests/subsys/rtio/rtio_i2c/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (c) 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(rtio_i2c_test) + +target_sources(app PRIVATE + src/blocking_emul.cpp + src/main.cpp +) + +target_include_directories(app PRIVATE include) diff --git a/tests/subsys/rtio/rtio_i2c/boards/native_sim.overlay b/tests/subsys/rtio/rtio_i2c/boards/native_sim.overlay new file mode 100644 index 00000000000..90f7e526452 --- /dev/null +++ b/tests/subsys/rtio/rtio_i2c/boards/native_sim.overlay @@ -0,0 +1,10 @@ +/* Copyright (c) 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +&i2c0 { + blocking_emul: blocking-emul@80 { + compatible = "zephyr,blocking-emul"; + reg = <0x80>; + }; +}; diff --git a/tests/subsys/rtio/rtio_i2c/dts/bindings/zephyr,blocking-emul.yaml b/tests/subsys/rtio/rtio_i2c/dts/bindings/zephyr,blocking-emul.yaml new file mode 100644 index 00000000000..7fc58028a8f --- /dev/null +++ b/tests/subsys/rtio/rtio_i2c/dts/bindings/zephyr,blocking-emul.yaml @@ -0,0 +1,9 @@ +# Copyright (c) 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 + +description: | + A generic blocking target device used for testing async RTIO APIs + +compatible: "zephyr,blocking-emul" + +include: i2c-device.yaml diff --git a/tests/subsys/rtio/rtio_i2c/include/blocking_emul.hpp b/tests/subsys/rtio/rtio_i2c/include/blocking_emul.hpp new file mode 100644 index 00000000000..66464631718 --- /dev/null +++ b/tests/subsys/rtio/rtio_i2c/include/blocking_emul.hpp @@ -0,0 +1,32 @@ +/* Copyright (c) 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +/* + * We need to override fff's function signature away from a standard function pointer. + * By using std::function<> we're able to leverage capture variables since additional storage must + * be allocated for them. In the tests, we will include asserts in the lambda functions. This + * provides the ability to assert inside the lambda and get errors that map to a line within the + * test function. If we use plain function pointers, we have to move the expected value outside + * of the test function and make it a static global of the compilational unit. + * + * Example: + * @code + * const uint8_t expected_value = 40; + * my_function_fake.custom_fake = [&expected_value](uint8_t value) { + * zassert_equal(expected_value, value); + * }; + * my_function(expected_value); + * @endcode + */ +#define CUSTOM_FFF_FUNCTION_TEMPLATE(RETURN, FUNCNAME, ...) \ + std::function FUNCNAME + +#include + +DECLARE_FAKE_VALUE_FUNC(int, blocking_emul_i2c_transfer, const struct emul *, struct i2c_msg *, int, + int); diff --git a/tests/subsys/rtio/rtio_i2c/prj.conf b/tests/subsys/rtio/rtio_i2c/prj.conf new file mode 100644 index 00000000000..bbb379751ab --- /dev/null +++ b/tests/subsys/rtio/rtio_i2c/prj.conf @@ -0,0 +1,24 @@ +# Copyright (c) 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 + +# Features being tested +CONFIG_LOG=y +CONFIG_RTIO=y +CONFIG_I2C=y +CONFIG_I2C_RTIO=y + +# Testing +CONFIG_ZTEST=y +CONFIG_EMUL=y + +# The number of pending requests allowed +CONFIG_RTIO_WORKQ_POOL_ITEMS=2 + +# Used for debugging +CONFIG_I2C_LOG_LEVEL_DBG=y +CONFIG_I2C_DUMP_MESSAGES=y + +# Language features for capturing lambdas +CONFIG_CPP=y +CONFIG_STD_CPP17=y +CONFIG_REQUIRES_FULL_LIBCPP=y diff --git a/tests/subsys/rtio/rtio_i2c/src/blocking_emul.cpp b/tests/subsys/rtio/rtio_i2c/src/blocking_emul.cpp new file mode 100644 index 00000000000..71ef35af3c6 --- /dev/null +++ b/tests/subsys/rtio/rtio_i2c/src/blocking_emul.cpp @@ -0,0 +1,38 @@ +/* Copyright (c) 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "blocking_emul.hpp" + +#include +#include +#include +#include +#include + +#define DT_DRV_COMPAT zephyr_blocking_emul + +LOG_MODULE_REGISTER(blocking_emul, CONFIG_I2C_LOG_LEVEL); + +DEFINE_FAKE_VALUE_FUNC(int, blocking_emul_i2c_transfer, const struct emul *, struct i2c_msg *, int, + int); + +static struct i2c_emul_api blocking_emul_i2c_api = { + .transfer = blocking_emul_i2c_transfer, +}; + +static int blocking_emul_init(const struct emul *target, const struct device *parent) +{ + return 0; +} + +static int blocking_dev_init(const struct device *dev) +{ + return 0; +} + +#define DECLARE_DRV(n) \ + EMUL_DT_INST_DEFINE(n, blocking_emul_init, NULL, NULL, &blocking_emul_i2c_api, NULL); \ + DEVICE_DT_INST_DEFINE(n, blocking_dev_init, NULL, NULL, NULL, POST_KERNEL, 99, NULL); + +DT_INST_FOREACH_STATUS_OKAY(DECLARE_DRV) diff --git a/tests/subsys/rtio/rtio_i2c/src/main.cpp b/tests/subsys/rtio/rtio_i2c/src/main.cpp new file mode 100644 index 00000000000..7ffeae0feb1 --- /dev/null +++ b/tests/subsys/rtio/rtio_i2c/src/main.cpp @@ -0,0 +1,347 @@ +/* Copyright (c) 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "blocking_emul.hpp" + +#include +#include +#include +#include + +DEFINE_FFF_GLOBALS; + +static const struct device *i2c_dev = DEVICE_DT_GET(DT_NODELABEL(i2c0)); +I2C_DT_IODEV_DEFINE(blocking_emul_iodev, DT_NODELABEL(blocking_emul)); + +RTIO_DEFINE(test_rtio_ctx, 4, 4); + +static void rtio_i2c_before(void *fixture) +{ + ARG_UNUSED(fixture); + RESET_FAKE(blocking_emul_i2c_transfer); + FFF_RESET_HISTORY(); + + rtio_sqe_drop_all(&test_rtio_ctx); + + struct rtio_cqe *cqe; + + while ((cqe = rtio_cqe_consume(&test_rtio_ctx)) != NULL) { + rtio_cqe_release(&test_rtio_ctx, cqe); + } +} + +ZTEST_SUITE(rtio_i2c, NULL, NULL, rtio_i2c_before, NULL, NULL); + +ZTEST(rtio_i2c, test_emulated_api_uses_fallback_submit) +{ + zassert_not_null(i2c_dev->api); + zassert_equal_ptr(i2c_iodev_submit_fallback, + ((const struct i2c_driver_api *)i2c_dev->api)->iodev_submit); +} + +ZTEST(rtio_i2c, test_fallback_submit_tx) +{ + uint8_t data[] = {0x01, 0x02, 0x03}; + struct i2c_msg msg = { + .buf = data, + .len = ARRAY_SIZE(data), + .flags = I2C_MSG_WRITE | I2C_MSG_STOP, + }; + + blocking_emul_i2c_transfer_fake.custom_fake = + [&msg](const struct emul *, struct i2c_msg *msgs, int msg_count, int) { + zassert_equal(1, msg_count); + zassert_equal(msg.len, msgs[0].len); + zassert_mem_equal(msg.buf, msgs[0].buf, msg.len); + zassert_equal(msg.flags, msgs[0].flags); + return 0; + }; + + struct rtio_sqe *sqe = i2c_rtio_copy(&test_rtio_ctx, &blocking_emul_iodev, &msg, 1); + + zassert_not_null(sqe); + zassert_ok(rtio_submit(&test_rtio_ctx, 1)); + zassert_equal(1, blocking_emul_i2c_transfer_fake.call_count); + + struct rtio_cqe *cqe = rtio_cqe_consume_block(&test_rtio_ctx); + + zassert_ok(cqe->result); + rtio_cqe_release(&test_rtio_ctx, cqe); +} + +ZTEST(rtio_i2c, test_fallback_submit_invalid_op) +{ + struct rtio_sqe *sqe = rtio_sqe_acquire(&test_rtio_ctx); + + zassert_not_null(sqe); + sqe->op = UINT8_MAX; + sqe->prio = RTIO_PRIO_NORM; + sqe->iodev = &blocking_emul_iodev; + sqe->userdata = NULL; + + zassert_ok(rtio_submit(&test_rtio_ctx, 1)); + zassert_equal(0, blocking_emul_i2c_transfer_fake.call_count); + + struct rtio_cqe *cqe = rtio_cqe_consume_block(&test_rtio_ctx); + + zassert_equal(-EIO, cqe->result); + rtio_cqe_release(&test_rtio_ctx, cqe); +} + +ZTEST(rtio_i2c, test_fallback_submit_tiny_tx) +{ + uint8_t data[] = {0x01, 0x02, 0x03}; + struct rtio_sqe *sqe = rtio_sqe_acquire(&test_rtio_ctx); + + blocking_emul_i2c_transfer_fake.custom_fake = + [&data](const struct emul *, struct i2c_msg *msgs, int msg_count, int) { + zassert_equal(1, msg_count); + zassert_equal(ARRAY_SIZE(data), msgs[0].len); + zassert_mem_equal(data, msgs[0].buf, msgs[0].len); + zassert_equal(I2C_MSG_WRITE | I2C_MSG_STOP, msgs[0].flags); + return 0; + }; + + zassert_not_null(sqe); + + rtio_sqe_prep_tiny_write(sqe, &blocking_emul_iodev, RTIO_PRIO_NORM, data, ARRAY_SIZE(data), + NULL); + zassert_ok(rtio_submit(&test_rtio_ctx, 1)); + zassert_equal(1, blocking_emul_i2c_transfer_fake.call_count); + + struct rtio_cqe *cqe = rtio_cqe_consume_block(&test_rtio_ctx); + + zassert_ok(cqe->result); + + rtio_cqe_release(&test_rtio_ctx, cqe); +} + +ZTEST(rtio_i2c, test_fallback_submit_txrx) +{ + uint8_t tx_data[] = {0x01, 0x02, 0x03}; + uint8_t rx_data[ARRAY_SIZE(tx_data)] = {0}; + struct rtio_sqe *sqe = rtio_sqe_acquire(&test_rtio_ctx); + + blocking_emul_i2c_transfer_fake.custom_fake = + [&tx_data](const struct emul *, struct i2c_msg *msgs, int msg_count, int) { + zassert_equal(2, msg_count); + // First message should be a 'tx' + zassert_equal(ARRAY_SIZE(tx_data), msgs[0].len); + zassert_mem_equal(tx_data, msgs[0].buf, msgs[0].len); + zassert_equal(I2C_MSG_WRITE, msgs[0].flags); + // Second message should be an 'rx' + zassert_equal(ARRAY_SIZE(tx_data), msgs[1].len); + zassert_equal(I2C_MSG_READ | I2C_MSG_STOP, msgs[1].flags); + for (uint8_t i = 0; i < msgs[1].len; ++i) { + msgs[1].buf[i] = msgs[0].buf[i]; + } + return 0; + }; + + zassert_not_null(sqe); + rtio_sqe_prep_transceive(sqe, &blocking_emul_iodev, RTIO_PRIO_NORM, tx_data, rx_data, + ARRAY_SIZE(tx_data), NULL); + zassert_ok(rtio_submit(&test_rtio_ctx, 1)); + zassert_equal(1, blocking_emul_i2c_transfer_fake.call_count); + + struct rtio_cqe *cqe = rtio_cqe_consume_block(&test_rtio_ctx); + + zassert_ok(cqe->result); + zassert_mem_equal(tx_data, rx_data, ARRAY_SIZE(tx_data)); + + rtio_cqe_release(&test_rtio_ctx, cqe); +} + +ZTEST(rtio_i2c, test_fallback_submit_rx) +{ + uint8_t expected_buffer[] = {0x00, 0x01, 0x02}; + uint8_t buffer[ARRAY_SIZE(expected_buffer)] = {0}; + struct i2c_msg msg = { + .buf = buffer, + .len = ARRAY_SIZE(buffer), + .flags = I2C_MSG_READ | I2C_MSG_STOP, + }; + + blocking_emul_i2c_transfer_fake.custom_fake = + [&msg](const struct emul *, struct i2c_msg *msgs, int msg_count, int) { + zassert_equal(1, msg_count); + zassert_equal(msg.len, msgs[0].len); + zassert_equal(msg.flags, msgs[0].flags); + for (uint8_t i = 0; i < msg.len; ++i) { + msgs[0].buf[i] = i; + } + return 0; + }; + + struct rtio_sqe *sqe = i2c_rtio_copy(&test_rtio_ctx, &blocking_emul_iodev, &msg, 1); + + zassert_not_null(sqe); + zassert_ok(rtio_submit(&test_rtio_ctx, 1)); + zassert_equal(1, blocking_emul_i2c_transfer_fake.call_count); + + struct rtio_cqe *cqe = rtio_cqe_consume_block(&test_rtio_ctx); + + zassert_ok(cqe->result); + zassert_mem_equal(buffer, expected_buffer, ARRAY_SIZE(expected_buffer)); + + rtio_cqe_release(&test_rtio_ctx, cqe); +} + +ZTEST(rtio_i2c, test_fallback_transaction_error) +{ + uint8_t buffer[3]; + struct rtio_sqe *phase1 = rtio_sqe_acquire(&test_rtio_ctx); + struct rtio_sqe *phase2 = rtio_sqe_acquire(&test_rtio_ctx); + + blocking_emul_i2c_transfer_fake.return_val = -EIO; + + zassert_not_null(phase1); + zassert_not_null(phase2); + + rtio_sqe_prep_read(phase1, &blocking_emul_iodev, RTIO_PRIO_NORM, buffer, ARRAY_SIZE(buffer), + NULL); + rtio_sqe_prep_read(phase2, &blocking_emul_iodev, RTIO_PRIO_NORM, buffer, ARRAY_SIZE(buffer), + NULL); + + phase1->flags |= RTIO_SQE_TRANSACTION; + + zassert_ok(rtio_submit(&test_rtio_ctx, 2)); + zassert_equal(1, blocking_emul_i2c_transfer_fake.call_count); + + struct rtio_cqe *cqe = rtio_cqe_consume_block(&test_rtio_ctx); + + zassert_equal(-EIO, cqe->result); + + rtio_cqe_release(&test_rtio_ctx, cqe); + + // We have another CQE for the transaction that must be cleared out. + cqe = rtio_cqe_consume_block(&test_rtio_ctx); + rtio_cqe_release(&test_rtio_ctx, cqe); +} + +ZTEST(rtio_i2c, test_fallback_transaction) +{ + uint8_t buffer[3]; + struct rtio_sqe *phase1 = rtio_sqe_acquire(&test_rtio_ctx); + struct rtio_sqe *phase2 = rtio_sqe_acquire(&test_rtio_ctx); + + zassert_not_null(phase1); + zassert_not_null(phase2); + + rtio_sqe_prep_read(phase1, &blocking_emul_iodev, RTIO_PRIO_NORM, buffer, ARRAY_SIZE(buffer), + NULL); + rtio_sqe_prep_read(phase2, &blocking_emul_iodev, RTIO_PRIO_NORM, buffer, ARRAY_SIZE(buffer), + NULL); + + phase1->flags |= RTIO_SQE_TRANSACTION; + + zassert_ok(rtio_submit(&test_rtio_ctx, 2)); + zassert_equal(2, blocking_emul_i2c_transfer_fake.call_count); + + struct rtio_cqe *cqe; + + // Check the first part of the transaction. + cqe = rtio_cqe_consume_block(&test_rtio_ctx); + zassert_ok(cqe->result); + rtio_cqe_release(&test_rtio_ctx, cqe); + + // We have another CQE for the transaction that must be cleared out. + cqe = rtio_cqe_consume_block(&test_rtio_ctx); + zassert_ok(cqe->result); + rtio_cqe_release(&test_rtio_ctx, cqe); +} + +ZTEST(rtio_i2c, test_work_queue_overflow) +{ + BUILD_ASSERT(CONFIG_RTIO_WORKQ_POOL_ITEMS == 2); + + uint8_t data[][2] = { + {0x01, 0x02}, + {0x03, 0x04}, + {0x05, 0x06}, + }; + struct i2c_msg msg[] = { + { + .buf = data[0], + .len = 2, + .flags = I2C_MSG_WRITE | I2C_MSG_STOP, + }, + { + .buf = data[1], + .len = 2, + .flags = I2C_MSG_READ | I2C_MSG_STOP, + }, + { + .buf = data[2], + .len = 2, + .flags = I2C_MSG_READ | I2C_MSG_ADDR_10_BITS | I2C_MSG_STOP, + }, + }; + + BUILD_ASSERT(ARRAY_SIZE(data) == ARRAY_SIZE(msg)); + + blocking_emul_i2c_transfer_fake.custom_fake = + [&msg](const struct emul *, struct i2c_msg *msgs, int msg_count, int) { + zassert_equal(1, msg_count); + + int msg_idx = i2c_is_read_op(&msgs[0]) ? 1 : 0; + + zassert_equal(msg[msg_idx].len, msgs[0].len); + zassert_mem_equal(msg[msg_idx].buf, msgs[0].buf, msg[msg_idx].len, + "Expected [0x%02x, 0x%02x] but got [0x%02x, 0x%02x]", + msg[msg_idx].buf[0], msg[msg_idx].buf[1], msgs[0].buf[0], + msgs[0].buf[1]); + zassert_equal(msg[msg_idx].flags, msgs[0].flags); + return 0; + }; + + struct rtio_sqe *sqe_write = + i2c_rtio_copy(&test_rtio_ctx, &blocking_emul_iodev, &msg[0], 1); + struct rtio_sqe *sqe_read = i2c_rtio_copy(&test_rtio_ctx, &blocking_emul_iodev, &msg[1], 1); + struct rtio_sqe *sqe_dropped = + i2c_rtio_copy(&test_rtio_ctx, &blocking_emul_iodev, &msg[2], 1); + + zassert_not_null(sqe_write); + zassert_not_null(sqe_read); + zassert_not_null(sqe_dropped); + + /* Add userdata so we can match these up with the CQEs */ + sqe_write->userdata = &msg[0]; + sqe_read->userdata = &msg[1]; + sqe_dropped->userdata = &msg[2]; + + zassert_ok(rtio_submit(&test_rtio_ctx, 3)); + zassert_equal(2, blocking_emul_i2c_transfer_fake.call_count); + + struct rtio_cqe *cqe[] = { + rtio_cqe_consume_block(&test_rtio_ctx), + rtio_cqe_consume_block(&test_rtio_ctx), + rtio_cqe_consume_block(&test_rtio_ctx), + }; + + /* + * We need to make sure that we got back results for all 3 messages and that there are no + * duplicates + */ + uint8_t msg_seen_mask = 0; + for (unsigned int i = 0; i < ARRAY_SIZE(cqe); ++i) { + int msg_idx = (struct i2c_msg *)cqe[i]->userdata - msg; + + zassert_true(msg_idx >= 0 && msg_idx < 3); + msg_seen_mask |= BIT(msg_idx); + if (msg_idx == 0 || msg_idx == 1) { + /* Expect the first 2 to succeed */ + zassert_ok(cqe[i]->result); + } else { + zassert_equal(-ENOMEM, cqe[i]->result); + } + } + + /* Make sure bits 0, 1, and 2 were set. */ + zassert_equal(0x7, msg_seen_mask); + + rtio_cqe_release(&test_rtio_ctx, cqe[0]); + rtio_cqe_release(&test_rtio_ctx, cqe[1]); + rtio_cqe_release(&test_rtio_ctx, cqe[2]); +} diff --git a/tests/subsys/rtio/rtio_i2c/testcase.yaml b/tests/subsys/rtio/rtio_i2c/testcase.yaml new file mode 100644 index 00000000000..3dfe7980a2d --- /dev/null +++ b/tests/subsys/rtio/rtio_i2c/testcase.yaml @@ -0,0 +1,7 @@ +# Copyright (c) 2024 Google LLC +# SPDX-License-Identifier: Apache-2.0 + +tests: + rtio.i2c: + platform_allow: native_sim + tags: rtio