From 8974c248cf1f87fda1d6730d8afcafd1ce0c3979 Mon Sep 17 00:00:00 2001 From: Yuval Peress Date: Mon, 1 Jul 2024 01:43:41 -0600 Subject: [PATCH] rtio: Add default i2c submit handler Use the RTIO work queue to fake the i2c submit calls for drivers which haven't yet implemented the API. Applications can change the size of the work queue pool depending on how much traffic they have on the buses. Signed-off-by: Yuval Peress --- doc/services/rtio/index.rst | 14 + drivers/i2c/CMakeLists.txt | 5 +- drivers/i2c/Kconfig | 10 +- drivers/i2c/Kconfig.nrfx | 1 - drivers/i2c/Kconfig.sam_twihs | 1 - drivers/i2c/gpio_i2c_switch.c | 3 + drivers/i2c/i2c_ambiq.c | 3 + drivers/i2c/i2c_andes_atciic100.c | 13 +- drivers/i2c/i2c_b91.c | 3 + drivers/i2c/i2c_bcm_iproc.c | 3 + drivers/i2c/i2c_cc13xx_cc26xx.c | 5 +- drivers/i2c/i2c_cc32xx.c | 4 +- drivers/i2c/i2c_dw.c | 3 + drivers/i2c/i2c_emul.c | 3 + drivers/i2c/i2c_ene_kb1200.c | 3 + drivers/i2c/i2c_esp32.c | 5 +- drivers/i2c/i2c_gd32.c | 3 + drivers/i2c/i2c_gecko.c | 3 + drivers/i2c/i2c_gpio.c | 3 + drivers/i2c/i2c_ifx_cat1.c | 6 +- drivers/i2c/i2c_ifx_xmc4.c | 6 +- drivers/i2c/i2c_imx.c | 3 + drivers/i2c/i2c_ite_enhance.c | 3 + drivers/i2c/i2c_ite_it8xxx2.c | 3 + drivers/i2c/i2c_litex.c | 3 + drivers/i2c/i2c_ll_stm32.c | 4 +- drivers/i2c/i2c_lpc11u6x.c | 3 + drivers/i2c/i2c_max32.c | 3 + drivers/i2c/i2c_mchp_mss.c | 3 + drivers/i2c/i2c_mchp_xec.c | 3 + drivers/i2c/i2c_mchp_xec_v2.c | 3 + drivers/i2c/i2c_mcux.c | 3 + drivers/i2c/i2c_mcux_flexcomm.c | 3 + drivers/i2c/i2c_nios2.c | 3 + drivers/i2c/i2c_npcx_port.c | 3 + drivers/i2c/i2c_nrfx_twim.c | 7 +- drivers/i2c/i2c_numaker.c | 3 + drivers/i2c/i2c_rcar.c | 3 + drivers/i2c/i2c_rtio_default.c | 145 ++++++++ drivers/i2c/i2c_rv32m1_lpi2c.c | 5 +- drivers/i2c/i2c_sam0.c | 4 +- drivers/i2c/i2c_sam4l_twim.c | 3 + drivers/i2c/i2c_sam_twi.c | 3 + drivers/i2c/i2c_sbcon.c | 3 + drivers/i2c/i2c_sc18im704.c | 3 + drivers/i2c/i2c_sedi.c | 5 +- drivers/i2c/i2c_sifive.c | 4 +- drivers/i2c/i2c_smartbond.c | 3 + drivers/i2c/i2c_tca954x.c | 3 + drivers/i2c/i2c_xilinx_axi.c | 3 + include/zephyr/drivers/i2c.h | 16 + tests/drivers/i2c/i2c_ram/testcase.yaml | 6 +- tests/subsys/rtio/rtio_i2c/CMakeLists.txt | 13 + .../rtio/rtio_i2c/boards/native_sim.overlay | 10 + .../dts/bindings/zephyr,blocking-emul.yaml | 9 + .../rtio/rtio_i2c/include/blocking_emul.hpp | 32 ++ tests/subsys/rtio/rtio_i2c/prj.conf | 24 ++ .../rtio/rtio_i2c/src/blocking_emul.cpp | 38 ++ tests/subsys/rtio/rtio_i2c/src/main.cpp | 347 ++++++++++++++++++ tests/subsys/rtio/rtio_i2c/testcase.yaml | 7 + 60 files changed, 811 insertions(+), 31 deletions(-) create mode 100644 drivers/i2c/i2c_rtio_default.c create mode 100644 tests/subsys/rtio/rtio_i2c/CMakeLists.txt create mode 100644 tests/subsys/rtio/rtio_i2c/boards/native_sim.overlay create mode 100644 tests/subsys/rtio/rtio_i2c/dts/bindings/zephyr,blocking-emul.yaml create mode 100644 tests/subsys/rtio/rtio_i2c/include/blocking_emul.hpp create mode 100644 tests/subsys/rtio/rtio_i2c/prj.conf create mode 100644 tests/subsys/rtio/rtio_i2c/src/blocking_emul.cpp create mode 100644 tests/subsys/rtio/rtio_i2c/src/main.cpp create mode 100644 tests/subsys/rtio/rtio_i2c/testcase.yaml 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