From e71546a24b776f10ece9f42d93306274de98fd46 Mon Sep 17 00:00:00 2001 From: Henrik Brix Andersen Date: Wed, 14 Feb 2024 22:38:34 +0100 Subject: [PATCH 1/2] drivers: can: rework support for manual bus-off recovery Since all CAN controllers drivers seem to support automatic recovery (for any future drivers for hardware without this hardware capability this can easily be implemented in the driver), change the Zephyr CAN controller API policy to: - Always enable automatic bus recovery upon driver initialization, regardless of Kconfig options. Since CAN controllers are initialized in "stopped" state, no unwanted bus-off recovery will be started at this point. - Invert and rename the Kconfig CONFIG_CAN_AUTO_BUS_OFF_RECOVERY, which is enabled by default, to CONFIG_CAN_MANUAL_RECOVERY_MODE, which is disabled by default. Enabling CONFIG_CAN_MANUAL_RECOVERY_MODE=y enables support for the can_recover() API function and a new manual recovery mode (see next bullet). Keeping this guarded by Kconfig allows keeping the flash footprint down for applications not using manual bus-off recovery. - Introduce a new CAN controller operational mode CAN_MODE_MANUAL_RECOVERY. Support for this is only enabled if CONFIG_CAN_MANUAL_RECOVERY_MODE=y. Having this as a mode allows applications to inquire whether the CAN controller supports manual recovery mode via the can_get_capabilities() API function and either fail or rely on automatic recovery - and it allows CAN controller drivers not supporting manual recovery mode to fail early in can_set_mode() during application startup instead of failing when can_recover() is called at a later point in time. Signed-off-by: Henrik Brix Andersen --- doc/hardware/peripherals/can/shell.rst | 11 ++--- drivers/can/Kconfig | 17 +++---- drivers/can/can_esp32_twai.c | 4 +- drivers/can/can_fake.c | 4 +- drivers/can/can_handlers.c | 7 +-- drivers/can/can_kvaser_pci.c | 4 +- drivers/can/can_loopback.c | 18 ------- drivers/can/can_mcan.c | 37 ++++++++++----- drivers/can/can_mcp2515.c | 18 ------- drivers/can/can_mcp251xfd.c | 18 ------- drivers/can/can_mcux_flexcan.c | 39 +++++++++++---- drivers/can/can_mcux_mcan.c | 4 +- drivers/can/can_native_linux.c | 18 ------- drivers/can/can_numaker.c | 4 +- drivers/can/can_nxp_s32_canxl.c | 60 ++++++++++++++++-------- drivers/can/can_rcar.c | 37 +++++++++++---- drivers/can/can_sam.c | 4 +- drivers/can/can_sam0.c | 4 +- drivers/can/can_shell.c | 5 +- drivers/can/can_sja1000.c | 27 ++++++++--- drivers/can/can_stm32_bxcan.c | 38 +++++++++++---- drivers/can/can_stm32_fdcan.c | 4 +- drivers/can/can_stm32h7_fdcan.c | 4 +- drivers/can/can_tcan4x5x.c | 4 +- drivers/can/can_xmc4xxx.c | 18 ------- include/zephyr/drivers/can.h | 39 +++++++-------- include/zephyr/drivers/can/can_mcan.h | 4 +- include/zephyr/drivers/can/can_sja1000.h | 4 +- samples/drivers/can/counter/src/main.c | 10 ---- tests/drivers/build_all/can/prj.conf | 2 +- tests/drivers/can/api/prj.conf | 2 +- tests/drivers/can/api/src/classic.c | 57 ++++++++++++++++++++-- tests/drivers/can/shell/prj.conf | 2 +- tests/drivers/can/shell/src/main.c | 2 +- 34 files changed, 293 insertions(+), 237 deletions(-) diff --git a/doc/hardware/peripherals/can/shell.rst b/doc/hardware/peripherals/can/shell.rst index 5b161fde5d6f..d10759ac3024 100644 --- a/doc/hardware/peripherals/can/shell.rst +++ b/doc/hardware/peripherals/can/shell.rst @@ -33,8 +33,7 @@ The following :ref:`Kconfig ` options enable additional subcommands and * :kconfig:option:`CONFIG_CAN_STATS` enables printing of various statistics for the CAN controller in the ``can show`` subcommand. This depends on :kconfig:option:`CONFIG_STATS` being enabled as well. -* :kconfig:option:`CONFIG_CAN_AUTO_BUS_OFF_RECOVERY` enables the ``can recover`` subcommand when - disabled. +* :kconfig:option:`CONFIG_CAN_MANUAL_RECOVERY_MODE` enables the ``can recover`` subcommand. For example, building the :ref:`hello_world` sample for the :ref:`frdm_k64f` with the CAN shell and CAN statistics enabled: @@ -253,8 +252,8 @@ details on the supported arguments. Bus Recovery ************ -The ``can recover`` subcommand can be used for initiating recovery from a CAN bus-off event as shown -below: +The ``can recover`` subcommand can be used for initiating manual recovery from a CAN bus-off event +as shown below: .. code-block:: console @@ -265,5 +264,5 @@ The subcommand accepts an optional bus recovery timeout in milliseconds. If no t the command will wait indefinitely for the bus recovery to succeed. .. note:: - The ``recover`` subcommand is only available if - :kconfig:option:`CONFIG_CAN_AUTO_BUS_OFF_RECOVERY` is disabled. + The ``recover`` subcommand is only available if :kconfig:option:`CONFIG_CAN_MANUAL_RECOVERY_MODE` + is enabled. diff --git a/drivers/can/Kconfig b/drivers/can/Kconfig index 702676c09a33..4f929ba32f83 100644 --- a/drivers/can/Kconfig +++ b/drivers/can/Kconfig @@ -62,10 +62,16 @@ config CAN_ACCEPT_RTR level. config CAN_FD_MODE - bool "CAN FD" + bool "CAN FD support" help Enable CAN FD support. Not all CAN controllers support CAN FD. +config CAN_MANUAL_RECOVERY_MODE + bool "Manual bus-off recovery support" + help + Enable support for manual (non-automatic) recovery from bus-off state. Not all CAN + controllers support manual recovery mode. + config CAN_RX_TIMESTAMP bool "Receiving timestamps" help @@ -73,15 +79,6 @@ config CAN_RX_TIMESTAMP The value is incremented every bit time and starts when the controller is initialized. Not all CAN controllers support timestamps. -config CAN_AUTO_BUS_OFF_RECOVERY - bool "Automatic recovery from bus-off" - default y - help - This option enables the automatic bus-off recovery according to - ISO 11898-1 (recovery after 128 occurrences of 11 consecutive - recessive bits). When this option is enabled, the recovery API is not - available. - config CAN_QEMU_IFACE_NAME string "SocketCAN interface name for QEMU" default "" diff --git a/drivers/can/can_esp32_twai.c b/drivers/can/can_esp32_twai.c index 11c30a2338c4..0f7eec519550 100644 --- a/drivers/can/can_esp32_twai.c +++ b/drivers/can/can_esp32_twai.c @@ -224,9 +224,9 @@ const struct can_driver_api can_esp32_twai_driver_api = { .set_state_change_callback = can_sja1000_set_state_change_callback, .get_core_clock = can_esp32_twai_get_core_clock, .get_max_filters = can_sja1000_get_max_filters, -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE .recover = can_sja1000_recover, -#endif /* !CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ .timing_min = CAN_SJA1000_TIMING_MIN_INITIALIZER, #ifdef CONFIG_SOC_SERIES_ESP32 .timing_max = CAN_SJA1000_TIMING_MAX_INITIALIZER, diff --git a/drivers/can/can_fake.c b/drivers/can/can_fake.c index 097a614a35e0..9da4ad80b236 100644 --- a/drivers/can/can_fake.c +++ b/drivers/can/can_fake.c @@ -103,9 +103,9 @@ static const struct can_driver_api fake_can_driver_api = { .add_rx_filter = fake_can_add_rx_filter, .remove_rx_filter = fake_can_remove_rx_filter, .get_state = fake_can_get_state, -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE .recover = fake_can_recover, -#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ .set_state_change_callback = fake_can_set_state_change_callback, .get_core_clock = fake_can_get_core_clock, .get_max_filters = fake_can_get_max_filters, diff --git a/drivers/can/can_handlers.c b/drivers/can/can_handlers.c index aa0a62674cb7..a5b728ca297b 100644 --- a/drivers/can/can_handlers.c +++ b/drivers/can/can_handlers.c @@ -249,15 +249,16 @@ static inline int z_vrfy_can_get_state(const struct device *dev, enum can_state } #include -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE static inline int z_vrfy_can_recover(const struct device *dev, k_timeout_t timeout) { - K_OOPS(K_SYSCALL_DRIVER_CAN(dev, recover)); + /* Optional API function */ + K_OOPS(K_SYSCALL_OBJ(dev, K_OBJ_DRIVER_CAN)); return z_impl_can_recover(dev, timeout); } #include -#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ #ifdef CONFIG_CAN_STATS diff --git a/drivers/can/can_kvaser_pci.c b/drivers/can/can_kvaser_pci.c index 909b29a0a34b..a1490416076d 100644 --- a/drivers/can/can_kvaser_pci.c +++ b/drivers/can/can_kvaser_pci.c @@ -143,9 +143,9 @@ const struct can_driver_api can_kvaser_pci_driver_api = { .set_state_change_callback = can_sja1000_set_state_change_callback, .get_core_clock = can_kvaser_pci_get_core_clock, .get_max_filters = can_sja1000_get_max_filters, -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE .recover = can_sja1000_recover, -#endif /* !CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ .timing_min = CAN_SJA1000_TIMING_MIN_INITIALIZER, .timing_max = CAN_SJA1000_TIMING_MAX_INITIALIZER, }; diff --git a/drivers/can/can_loopback.c b/drivers/can/can_loopback.c index ffab53b315ee..dcfb2a6cce86 100644 --- a/drivers/can/can_loopback.c +++ b/drivers/can/can_loopback.c @@ -340,21 +340,6 @@ static int can_loopback_get_state(const struct device *dev, enum can_state *stat return 0; } -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY -static int can_loopback_recover(const struct device *dev, k_timeout_t timeout) -{ - struct can_loopback_data *data = dev->data; - - ARG_UNUSED(timeout); - - if (!data->common.started) { - return -ENETDOWN; - } - - return 0; -} -#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ - static void can_loopback_set_state_change_callback(const struct device *dev, can_state_change_callback_t cb, void *user_data) @@ -388,9 +373,6 @@ static const struct can_driver_api can_loopback_driver_api = { .add_rx_filter = can_loopback_add_rx_filter, .remove_rx_filter = can_loopback_remove_rx_filter, .get_state = can_loopback_get_state, -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY - .recover = can_loopback_recover, -#endif .set_state_change_callback = can_loopback_set_state_change_callback, .get_core_clock = can_loopback_get_core_clock, .get_max_filters = can_loopback_get_max_filters, diff --git a/drivers/can/can_mcan.c b/drivers/can/can_mcan.c index df77962232c8..2e33bda2d618 100644 --- a/drivers/can/can_mcan.c +++ b/drivers/can/can_mcan.c @@ -258,9 +258,13 @@ int can_mcan_get_capabilities(const struct device *dev, can_mode_t *cap) *cap = CAN_MODE_NORMAL | CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY; -#if CONFIG_CAN_FD_MODE - *cap |= CAN_MODE_FD; -#endif /* CONFIG_CAN_FD_MODE */ + if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) { + *cap |= CAN_MODE_MANUAL_RECOVERY; + } + + if (IS_ENABLED(CONFIG_CAN_FD_MODE)) { + *cap |= CAN_MODE_FD; + } return 0; } @@ -350,22 +354,24 @@ int can_mcan_stop(const struct device *dev) int can_mcan_set_mode(const struct device *dev, can_mode_t mode) { + can_mode_t supported = CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY; struct can_mcan_data *data = dev->data; uint32_t cccr; uint32_t test; int err; -#ifdef CONFIG_CAN_FD_MODE - if ((mode & ~(CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | CAN_MODE_FD)) != 0U) { - LOG_ERR("unsupported mode: 0x%08x", mode); - return -ENOTSUP; + if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) { + supported |= CAN_MODE_MANUAL_RECOVERY; } -#else /* CONFIG_CAN_FD_MODE */ - if ((mode & ~(CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY)) != 0U) { + + if (IS_ENABLED(CONFIG_CAN_FD_MODE)) { + supported |= CAN_MODE_FD; + } + + if ((mode & ~(supported)) != 0U) { LOG_ERR("unsupported mode: 0x%08x", mode); return -ENOTSUP; } -#endif /* !CONFIG_CAN_FD_MODE */ if (data->common.started) { return -EBUSY; @@ -462,7 +468,8 @@ static void can_mcan_state_change_handler(const struct device *dev) } } - if (IS_ENABLED(CONFIG_CAN_AUTO_BUS_OFF_RECOVERY)) { + if (!IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE) || + (data->common.mode & CAN_MODE_MANUAL_RECOVERY) == 0U) { /* * Request leaving init mode, but do not take the lock (as we are in ISR * context), nor wait for the result. @@ -847,7 +854,7 @@ int can_mcan_get_state(const struct device *dev, enum can_state *state, return 0; } -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE int can_mcan_recover(const struct device *dev, k_timeout_t timeout) { struct can_mcan_data *data = dev->data; @@ -856,9 +863,13 @@ int can_mcan_recover(const struct device *dev, k_timeout_t timeout) return -ENETDOWN; } + if ((data->common.mode & CAN_MODE_MANUAL_RECOVERY) == 0U) { + return -ENOTSUP; + } + return can_mcan_leave_init_mode(dev, timeout); } -#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ int can_mcan_send(const struct device *dev, const struct can_frame *frame, k_timeout_t timeout, can_tx_callback_t callback, void *user_data) diff --git a/drivers/can/can_mcp2515.c b/drivers/can/can_mcp2515.c index 13693f69c774..692015cdff3d 100644 --- a/drivers/can/can_mcp2515.c +++ b/drivers/can/can_mcp2515.c @@ -789,21 +789,6 @@ static void mcp2515_handle_errors(const struct device *dev) } } -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY -static int mcp2515_recover(const struct device *dev, k_timeout_t timeout) -{ - struct mcp2515_data *dev_data = dev->data; - - ARG_UNUSED(timeout); - - if (!dev_data->common.started) { - return -ENETDOWN; - } - - return -ENOTSUP; -} -#endif - static void mcp2515_handle_interrupts(const struct device *dev) { const struct mcp2515_config *dev_cfg = dev->config; @@ -904,9 +889,6 @@ static const struct can_driver_api can_api_funcs = { .add_rx_filter = mcp2515_add_rx_filter, .remove_rx_filter = mcp2515_remove_rx_filter, .get_state = mcp2515_get_state, -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY - .recover = mcp2515_recover, -#endif .set_state_change_callback = mcp2515_set_state_change_callback, .get_core_clock = mcp2515_get_core_clock, .get_max_filters = mcp2515_get_max_filters, diff --git a/drivers/can/can_mcp251xfd.c b/drivers/can/can_mcp251xfd.c index 97e16bf0652d..04ee18bba2a0 100644 --- a/drivers/can/can_mcp251xfd.c +++ b/drivers/can/can_mcp251xfd.c @@ -740,21 +740,6 @@ static int mcp251xfd_get_max_filters(const struct device *dev, bool ide) return CONFIG_CAN_MAX_FILTER; } -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY -static int mcp251xfd_recover(const struct device *dev, k_timeout_t timeout) -{ - struct mcp251xfd_data *dev_data = dev->data; - - ARG_UNUSED(timeout); - - if (!dev_data->common.started) { - return -ENETDOWN; - } - - return -ENOTSUP; -} -#endif - static int mcp251xfd_handle_fifo_read(const struct device *dev, const struct mcp251xfd_fifo *fifo, uint8_t fifo_type) { @@ -1646,9 +1631,6 @@ static const struct can_driver_api mcp251xfd_api_funcs = { .send = mcp251xfd_send, .add_rx_filter = mcp251xfd_add_rx_filter, .remove_rx_filter = mcp251xfd_remove_rx_filter, -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY - .recover = mcp251xfd_recover, -#endif .get_state = mcp251xfd_get_state, .set_state_change_callback = mcp251xfd_set_state_change_callback, .get_core_clock = mcp251xfd_get_core_clock, diff --git a/drivers/can/can_mcux_flexcan.c b/drivers/can/can_mcux_flexcan.c index 6f41e6dbb8b2..7a1e5dde4e37 100644 --- a/drivers/can/can_mcux_flexcan.c +++ b/drivers/can/can_mcux_flexcan.c @@ -177,6 +177,10 @@ static int mcux_flexcan_get_capabilities(const struct device *dev, can_mode_t *c *cap = CAN_MODE_NORMAL | CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | CAN_MODE_3_SAMPLES; + if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) { + *cap |= CAN_MODE_MANUAL_RECOVERY; + } + if (UTIL_AND(IS_ENABLED(CONFIG_CAN_MCUX_FLEXCAN_FD), config->flexcan_fd)) { *cap |= CAN_MODE_FD; } @@ -388,6 +392,10 @@ static int mcux_flexcan_set_mode(const struct device *dev, can_mode_t mode) return -EBUSY; } + if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) { + supported |= CAN_MODE_MANUAL_RECOVERY; + } + if (UTIL_AND(IS_ENABLED(CONFIG_CAN_MCUX_FLEXCAN_FD), config->flexcan_fd)) { supported |= CAN_MODE_FD; } @@ -431,6 +439,16 @@ static int mcux_flexcan_set_mode(const struct device *dev, can_mode_t mode) ctrl1 &= ~(CAN_CTRL1_SMP_MASK); } + if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) { + if ((mode & CAN_MODE_MANUAL_RECOVERY) != 0) { + /* Disable auto-recovery from bus-off */ + ctrl1 |= CAN_CTRL1_BOFFREC_MASK; + } else { + /* Enable auto-recovery from bus-off */ + ctrl1 &= ~(CAN_CTRL1_BOFFREC_MASK); + } + } + #ifdef CONFIG_CAN_MCUX_FLEXCAN_FD if (config->flexcan_fd) { if ((mode & CAN_MODE_FD) != 0) { @@ -819,7 +837,7 @@ static void mcux_flexcan_set_state_change_callback(const struct device *dev, data->common.state_change_cb_user_data = user_data; } -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE static int mcux_flexcan_recover(const struct device *dev, k_timeout_t timeout) { const struct mcux_flexcan_config *config = dev->config; @@ -832,6 +850,10 @@ static int mcux_flexcan_recover(const struct device *dev, k_timeout_t timeout) return -ENETDOWN; } + if ((data->common.mode & CAN_MODE_MANUAL_RECOVERY) == 0U) { + return -ENOTSUP; + } + (void)mcux_flexcan_get_state(dev, &state, NULL); if (state != CAN_STATE_BUS_OFF) { return 0; @@ -857,7 +879,7 @@ static int mcux_flexcan_recover(const struct device *dev, k_timeout_t timeout) return ret; } -#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ static void mcux_flexcan_remove_rx_filter(const struct device *dev, int filter_id) { @@ -1225,9 +1247,8 @@ static int mcux_flexcan_init(const struct device *dev) config->irq_config_func(dev); -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY - config->base->CTRL1 |= CAN_CTRL1_BOFFREC_MASK; -#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ + /* Enable auto-recovery from bus-off */ + config->base->CTRL1 &= ~(CAN_CTRL1_BOFFREC_MASK); (void)mcux_flexcan_get_state(dev, &data->state, NULL); @@ -1244,9 +1265,9 @@ __maybe_unused static const struct can_driver_api mcux_flexcan_driver_api = { .add_rx_filter = mcux_flexcan_add_rx_filter, .remove_rx_filter = mcux_flexcan_remove_rx_filter, .get_state = mcux_flexcan_get_state, -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE .recover = mcux_flexcan_recover, -#endif +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ .set_state_change_callback = mcux_flexcan_set_state_change_callback, .get_core_clock = mcux_flexcan_get_core_clock, .get_max_filters = mcux_flexcan_get_max_filters, @@ -1287,9 +1308,9 @@ static const struct can_driver_api mcux_flexcan_fd_driver_api = { .add_rx_filter = mcux_flexcan_add_rx_filter, .remove_rx_filter = mcux_flexcan_remove_rx_filter, .get_state = mcux_flexcan_get_state, -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE .recover = mcux_flexcan_recover, -#endif +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ .set_state_change_callback = mcux_flexcan_set_state_change_callback, .get_core_clock = mcux_flexcan_get_core_clock, .get_max_filters = mcux_flexcan_get_max_filters, diff --git a/drivers/can/can_mcux_mcan.c b/drivers/can/can_mcux_mcan.c index 28a709a023c0..2ca5d3e02771 100644 --- a/drivers/can/can_mcux_mcan.c +++ b/drivers/can/can_mcux_mcan.c @@ -132,9 +132,9 @@ static const struct can_driver_api mcux_mcan_driver_api = { .send = can_mcan_send, .add_rx_filter = can_mcan_add_rx_filter, .remove_rx_filter = can_mcan_remove_rx_filter, -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE .recover = can_mcan_recover, -#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ .get_state = can_mcan_get_state, .set_state_change_callback = can_mcan_set_state_change_callback, .get_core_clock = mcux_mcan_get_core_clock, diff --git a/drivers/can/can_native_linux.c b/drivers/can/can_native_linux.c index a2014fca0be7..9356588a4113 100644 --- a/drivers/can/can_native_linux.c +++ b/drivers/can/can_native_linux.c @@ -373,21 +373,6 @@ static int can_native_linux_get_state(const struct device *dev, enum can_state * return 0; } -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY -static int can_native_linux_recover(const struct device *dev, k_timeout_t timeout) -{ - struct can_native_linux_data *data = dev->data; - - ARG_UNUSED(timeout); - - if (!data->common.started) { - return -ENETDOWN; - } - - return 0; -} -#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ - static void can_native_linux_set_state_change_callback(const struct device *dev, can_state_change_callback_t cb, void *user_data) @@ -422,9 +407,6 @@ static const struct can_driver_api can_native_linux_driver_api = { .add_rx_filter = can_native_linux_add_rx_filter, .remove_rx_filter = can_native_linux_remove_rx_filter, .get_state = can_native_linux_get_state, -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY - .recover = can_native_linux_recover, -#endif .set_state_change_callback = can_native_linux_set_state_change_callback, .get_core_clock = can_native_linux_get_core_clock, .get_max_filters = can_native_linux_get_max_filters, diff --git a/drivers/can/can_numaker.c b/drivers/can/can_numaker.c index ba51b8b82fd3..7580708052ce 100644 --- a/drivers/can/can_numaker.c +++ b/drivers/can/can_numaker.c @@ -169,9 +169,9 @@ static const struct can_driver_api can_numaker_driver_api = { .send = can_mcan_send, .add_rx_filter = can_mcan_add_rx_filter, .remove_rx_filter = can_mcan_remove_rx_filter, -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE .recover = can_mcan_recover, -#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ .get_state = can_mcan_get_state, .set_state_change_callback = can_mcan_set_state_change_callback, .get_core_clock = can_numaker_get_core_clock, diff --git a/drivers/can/can_nxp_s32_canxl.c b/drivers/can/can_nxp_s32_canxl.c index 50464ac7a6a6..217d78981cae 100644 --- a/drivers/can/can_nxp_s32_canxl.c +++ b/drivers/can/can_nxp_s32_canxl.c @@ -123,9 +123,13 @@ static int can_nxp_s32_get_capabilities(const struct device *dev, can_mode_t *ca *cap = CAN_MODE_NORMAL | CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY; -#ifdef CAN_NXP_S32_FD_MODE - *cap |= CAN_MODE_FD; -#endif + if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) { + *cap |= CAN_MODE_MANUAL_RECOVERY; + } + + if (IS_ENABLED(CAN_NXP_S32_FD_MODE)) { + *cap |= CAN_MODE_FD; + } return 0; } @@ -271,6 +275,7 @@ static int can_nxp_s32_stop(const struct device *dev) static int can_nxp_s32_set_mode(const struct device *dev, can_mode_t mode) { + can_mode_t supported = CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY; const struct can_nxp_s32_config *config = dev->config; struct can_nxp_s32_data *data = dev->data; Canexcel_Ip_ModesType can_nxp_s32_mode = CAN_MODE_NORMAL; @@ -280,11 +285,16 @@ static int can_nxp_s32_set_mode(const struct device *dev, can_mode_t mode) if (data->common.started) { return -EBUSY; } -#ifdef CAN_NXP_S32_FD_MODE - if ((mode & ~(CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | CAN_MODE_FD)) != 0) { -#else - if ((mode & ~(CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY)) != 0) { -#endif + + if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) { + supported |= CAN_MODE_MANUAL_RECOVERY; + } + + if (IS_ENABLED(CAN_NXP_S32_FD_MODE)) { + supported |= CAN_MODE_FD; + } + + if ((mode & ~(supported)) != 0) { LOG_ERR("unsupported mode: 0x%08x", mode); return -ENOTSUP; } @@ -309,6 +319,20 @@ static int can_nxp_s32_set_mode(const struct device *dev, can_mode_t mode) CanXL_SetFDEnabled(config->base_sic, canfd, brs); + if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) { + Canexcel_Ip_StatusType status; + uint32_t options = 0U; + + if ((mode & CAN_MODE_MANUAL_RECOVERY) == 0U) { + options = CANXL_IP_BUSOFF_RECOVERY_U32; + } + + status = CanXL_ConfigCtrlOptions(config->base_sic, options); + if (status != CANEXCEL_STATUS_SUCCESS) { + return -EIO; + } + } + CanXL_SetOperationMode(config->base_sic, can_nxp_s32_mode); Canexcel_Ip_ExitFreezeMode(config->instance); @@ -377,7 +401,7 @@ static void can_nxp_s32_set_state_change_callback(const struct device *dev, data->common.state_change_cb_user_data = user_data; } -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE static int can_nxp_s32_recover(const struct device *dev, k_timeout_t timeout) { const struct can_nxp_s32_config *config = dev->config; @@ -390,6 +414,10 @@ static int can_nxp_s32_recover(const struct device *dev, k_timeout_t timeout) return -ENETDOWN; } + if ((data->common.mode & CAN_MODE_MANUAL_RECOVERY) == 0U) { + return -ENOTSUP; + } + can_nxp_s32_get_state(dev, &state, NULL); if (state != CAN_STATE_BUS_OFF) { return 0; @@ -415,7 +443,7 @@ static int can_nxp_s32_recover(const struct device *dev, k_timeout_t timeout) return ret; } -#endif +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ static void can_nxp_s32_remove_rx_filter(const struct device *dev, int filter_id) { @@ -1024,9 +1052,9 @@ static const struct can_driver_api can_nxp_s32_driver_api = { .add_rx_filter = can_nxp_s32_add_rx_filter, .remove_rx_filter = can_nxp_s32_remove_rx_filter, .get_state = can_nxp_s32_get_state, -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE .recover = can_nxp_s32_recover, -#endif +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ .set_state_change_callback = can_nxp_s32_set_state_change_callback, .get_core_clock = can_nxp_s32_get_core_clock, .get_max_filters = can_nxp_s32_get_max_filters, @@ -1102,12 +1130,6 @@ static const struct can_driver_api can_nxp_s32_driver_api = { #define CAN_NXP_S32_BRS 0 #endif -#ifdef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY -#define CAN_NXP_S32_CTRL_OPTIONS CANXL_IP_BUSOFF_RECOVERY_U32 -#else -#define CAN_NXP_S32_CTRL_OPTIONS 0 -#endif - #define CAN_NXP_S32_HW_INSTANCE_CHECK(i, n) \ ((DT_INST_REG_ADDR(n) == IP_CANXL_##i##__SIC_BASE) ? i : 0) @@ -1135,7 +1157,7 @@ static const struct can_driver_api can_nxp_s32_driver_api = { .CanxlMode = CANEXCEL_LISTEN_ONLY_MODE, \ .fd_enable = (boolean)IS_ENABLED(CAN_NXP_S32_FD_MODE), \ .bitRateSwitch = (boolean)CAN_NXP_S32_BRS, \ - .ctrlOptions = (uint32)CAN_NXP_S32_CTRL_OPTIONS, \ + .ctrlOptions = CANXL_IP_BUSOFF_RECOVERY_U32, \ .Callback = nxp_s32_can_##n##_ctrl_callback, \ .ErrorCallback = nxp_s32_can_##n##_err_callback, \ IF_ENABLED(CONFIG_CAN_NXP_S32_RX_FIFO, \ diff --git a/drivers/can/can_rcar.c b/drivers/can/can_rcar.c index 8ed931c4d105..b389b638a3b7 100644 --- a/drivers/can/can_rcar.c +++ b/drivers/can/can_rcar.c @@ -653,12 +653,17 @@ static int can_rcar_stop(const struct device *dev) static int can_rcar_set_mode(const struct device *dev, can_mode_t mode) { + can_mode_t supported = CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY; const struct can_rcar_cfg *config = dev->config; struct can_rcar_data *data = dev->data; uint8_t tcr = 0; int ret = 0; - if ((mode & ~(CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY)) != 0) { + if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) { + supported |= CAN_MODE_MANUAL_RECOVERY; + } + + if ((mode & ~(supported)) != 0) { LOG_ERR("Unsupported mode: 0x%08x", mode); return -ENOTSUP; } @@ -687,6 +692,20 @@ static int can_rcar_set_mode(const struct device *dev, can_mode_t mode) sys_write8(tcr, config->reg_addr + RCAR_CAN_TCR); + if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) { + uint16_t ctlr = can_rcar_read16(config, RCAR_CAN_CTLR); + + if ((mode & CAN_MODE_MANUAL_RECOVERY) != 0U) { + /* Set entry to halt automatically at bus-off */ + ctlr |= RCAR_CAN_CTLR_BOM_ENT; + } else { + /* Clear entry to halt automatically at bus-off */ + ctlr &= ~RCAR_CAN_CTLR_BOM_ENT; + } + + can_rcar_write16(config, RCAR_CAN_CTLR, ctlr); + } + data->common.mode = mode; unlock: @@ -805,7 +824,7 @@ static int can_rcar_get_state(const struct device *dev, enum can_state *state, return 0; } -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE static int can_rcar_recover(const struct device *dev, k_timeout_t timeout) { const struct can_rcar_cfg *config = dev->config; @@ -817,6 +836,10 @@ static int can_rcar_recover(const struct device *dev, k_timeout_t timeout) return -ENETDOWN; } + if ((data->common.mode & CAN_MODE_MANUAL_RECOVERY) == 0U) { + return -ENOTSUP; + } + if (data->state != CAN_STATE_BUS_OFF) { return 0; } @@ -843,7 +866,7 @@ static int can_rcar_recover(const struct device *dev, k_timeout_t timeout) k_mutex_unlock(&data->inst_mutex); return ret; } -#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ static int can_rcar_send(const struct device *dev, const struct can_frame *frame, k_timeout_t timeout, can_tx_callback_t callback, @@ -1078,9 +1101,7 @@ static int can_rcar_init(const struct device *dev) ctlr = can_rcar_read16(config, RCAR_CAN_CTLR); ctlr |= RCAR_CAN_CTLR_IDFM_MIXED; /* Select mixed ID mode */ -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY - ctlr |= RCAR_CAN_CTLR_BOM_ENT; /* Entry to halt mode automatically at bus-off */ -#endif + ctlr &= ~RCAR_CAN_CTLR_BOM_ENT; /* Clear entry to halt automatically at bus-off */ ctlr |= RCAR_CAN_CTLR_MBM; /* Select FIFO mailbox mode */ ctlr |= RCAR_CAN_CTLR_MLM; /* Overrun mode */ ctlr &= ~RCAR_CAN_CTLR_SLPM; /* Clear CAN Sleep mode */ @@ -1142,9 +1163,9 @@ static const struct can_driver_api can_rcar_driver_api = { .add_rx_filter = can_rcar_add_rx_filter, .remove_rx_filter = can_rcar_remove_rx_filter, .get_state = can_rcar_get_state, -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE .recover = can_rcar_recover, -#endif +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ .set_state_change_callback = can_rcar_set_state_change_callback, .get_core_clock = can_rcar_get_core_clock, .get_max_filters = can_rcar_get_max_filters, diff --git a/drivers/can/can_sam.c b/drivers/can/can_sam.c index 4f0f32bce914..a77c189ba0a5 100644 --- a/drivers/can/can_sam.c +++ b/drivers/can/can_sam.c @@ -126,9 +126,9 @@ static const struct can_driver_api can_sam_driver_api = { .add_rx_filter = can_mcan_add_rx_filter, .remove_rx_filter = can_mcan_remove_rx_filter, .get_state = can_mcan_get_state, -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE .recover = can_mcan_recover, -#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ .get_core_clock = can_sam_get_core_clock, .get_max_filters = can_mcan_get_max_filters, .set_state_change_callback = can_mcan_set_state_change_callback, diff --git a/drivers/can/can_sam0.c b/drivers/can/can_sam0.c index 0a8bacda05c8..adcbf6a567db 100644 --- a/drivers/can/can_sam0.c +++ b/drivers/can/can_sam0.c @@ -171,9 +171,9 @@ static const struct can_driver_api can_sam0_driver_api = { .add_rx_filter = can_mcan_add_rx_filter, .remove_rx_filter = can_mcan_remove_rx_filter, .get_state = can_mcan_get_state, -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE .recover = can_mcan_recover, -#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ .get_core_clock = can_sam0_get_core_clock, .get_max_filters = can_mcan_get_max_filters, .set_state_change_callback = can_mcan_set_state_change_callback, diff --git a/drivers/can/can_shell.c b/drivers/can/can_shell.c index 3bb74f9552c2..b6b64fe371ea 100644 --- a/drivers/can/can_shell.c +++ b/drivers/can/can_shell.c @@ -35,6 +35,7 @@ static const struct can_shell_mode_mapping can_shell_mode_map[] = { CAN_SHELL_MODE_MAPPING("normal", CAN_MODE_NORMAL), CAN_SHELL_MODE_MAPPING("one-shot", CAN_MODE_ONE_SHOT), CAN_SHELL_MODE_MAPPING("triple-sampling", CAN_MODE_3_SAMPLES), + CAN_SHELL_MODE_MAPPING("manual-recovery", CAN_MODE_MANUAL_RECOVERY), }; K_MSGQ_DEFINE(can_shell_tx_msgq, sizeof(struct can_shell_tx_event), @@ -1054,9 +1055,9 @@ SHELL_STATIC_SUBCMD_SET_CREATE(sub_can_cmds, "CAN rx filter commands\n" "Usage: can filter ...", NULL), - SHELL_EXPR_CMD_ARG(!IS_ENABLED(CONFIG_CAN_AUTO_BUS_OFF_RECOVERY), + SHELL_COND_CMD_ARG(CONFIG_CAN_MANUAL_RECOVERY_MODE, recover, &dsub_can_device_name, - "Recover CAN controller from bus-off state\n" + "Manually recover CAN controller from bus-off state\n" "Usage: can recover [timeout ms]", cmd_can_recover, 2, 1), SHELL_SUBCMD_SET_END diff --git a/drivers/can/can_sja1000.c b/drivers/can/can_sja1000.c index 39d823d0137d..b9cdc1468226 100644 --- a/drivers/can/can_sja1000.c +++ b/drivers/can/can_sja1000.c @@ -141,6 +141,10 @@ int can_sja1000_get_capabilities(const struct device *dev, can_mode_t *cap) *cap = CAN_MODE_NORMAL | CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | CAN_MODE_ONE_SHOT | CAN_MODE_3_SAMPLES; + if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) { + *cap |= CAN_MODE_MANUAL_RECOVERY; + } + return 0; } @@ -213,12 +217,17 @@ int can_sja1000_stop(const struct device *dev) int can_sja1000_set_mode(const struct device *dev, can_mode_t mode) { + can_mode_t supported = CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | CAN_MODE_ONE_SHOT | + CAN_MODE_3_SAMPLES; struct can_sja1000_data *data = dev->data; uint8_t btr1; uint8_t mod; - if ((mode & ~(CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | CAN_MODE_ONE_SHOT | - CAN_MODE_3_SAMPLES)) != 0) { + if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) { + supported |= CAN_MODE_MANUAL_RECOVERY; + } + + if ((mode & ~(supported)) != 0) { LOG_ERR("unsupported mode: 0x%08x", mode); return -ENOTSUP; } @@ -464,7 +473,7 @@ void can_sja1000_remove_rx_filter(const struct device *dev, int filter_id) } } -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE int can_sja1000_recover(const struct device *dev, k_timeout_t timeout) { struct can_sja1000_data *data = dev->data; @@ -476,6 +485,10 @@ int can_sja1000_recover(const struct device *dev, k_timeout_t timeout) return -ENETDOWN; } + if ((data->common.mode & CAN_MODE_MANUAL_RECOVERY) == 0U) { + return -ENOTSUP; + } + sr = can_sja1000_read_reg(dev, CAN_SJA1000_SR); if ((sr & CAN_SJA1000_SR_BS) == 0) { return 0; @@ -509,7 +522,7 @@ int can_sja1000_recover(const struct device *dev, k_timeout_t timeout) return 0; } -#endif /* !CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ int can_sja1000_get_state(const struct device *dev, enum can_state *state, struct can_bus_err_cnt *err_cnt) @@ -659,11 +672,11 @@ static void can_sja1000_handle_error_warning_irq(const struct device *dev) if ((sr & CAN_SJA1000_SR_BS) != 0) { data->state = CAN_STATE_BUS_OFF; can_sja1000_tx_done(dev, -ENETUNREACH); -#ifdef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY - if (data->common.started) { + + if (data->common.started && + (data->common.mode & CAN_MODE_MANUAL_RECOVERY) == 0U) { can_sja1000_leave_reset_mode_nowait(dev); } -#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ } else if ((sr & CAN_SJA1000_SR_ES) != 0) { data->state = CAN_STATE_ERROR_WARNING; } else { diff --git a/drivers/can/can_stm32_bxcan.c b/drivers/can/can_stm32_bxcan.c index 7d8de00d4e6c..212d7622d9c0 100644 --- a/drivers/can/can_stm32_bxcan.c +++ b/drivers/can/can_stm32_bxcan.c @@ -380,6 +380,10 @@ static int can_stm32_get_capabilities(const struct device *dev, can_mode_t *cap) *cap = CAN_MODE_NORMAL | CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | CAN_MODE_ONE_SHOT; + if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) { + *cap |= CAN_MODE_MANUAL_RECOVERY; + } + return 0; } @@ -473,13 +477,18 @@ static int can_stm32_stop(const struct device *dev) static int can_stm32_set_mode(const struct device *dev, can_mode_t mode) { + can_mode_t supported = CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | CAN_MODE_ONE_SHOT; const struct can_stm32_config *cfg = dev->config; CAN_TypeDef *can = cfg->can; struct can_stm32_data *data = dev->data; LOG_DBG("Set mode %d", mode); - if ((mode & ~(CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | CAN_MODE_ONE_SHOT)) != 0) { + if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) { + supported |= CAN_MODE_MANUAL_RECOVERY; + } + + if ((mode & ~(supported)) != 0) { LOG_ERR("unsupported mode: 0x%08x", mode); return -ENOTSUP; } @@ -511,6 +520,15 @@ static int can_stm32_set_mode(const struct device *dev, can_mode_t mode) can->MCR &= ~CAN_MCR_NART; } + if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) { + if ((mode & CAN_MODE_MANUAL_RECOVERY) != 0) { + /* No automatic recovery from bus-off */ + can->MCR &= ~CAN_MCR_ABOM; + } else { + can->MCR |= CAN_MCR_ABOM; + } + } + data->common.mode = mode; k_mutex_unlock(&data->inst_mutex); @@ -637,9 +655,10 @@ static int can_stm32_init(const struct device *dev) #ifdef CONFIG_CAN_RX_TIMESTAMP can->MCR |= CAN_MCR_TTCM; #endif -#ifdef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY + + /* Enable automatic bus-off recovery */ can->MCR |= CAN_MCR_ABOM; -#endif + ret = can_calc_timing(dev, &timing, cfg->common.bus_speed, cfg->common.sample_point); if (ret == -EINVAL) { @@ -686,7 +705,7 @@ static void can_stm32_set_state_change_callback(const struct device *dev, } } -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE static int can_stm32_recover(const struct device *dev, k_timeout_t timeout) { const struct can_stm32_config *cfg = dev->config; @@ -699,6 +718,10 @@ static int can_stm32_recover(const struct device *dev, k_timeout_t timeout) return -ENETDOWN; } + if ((data->common.mode & CAN_MODE_MANUAL_RECOVERY) == 0U) { + return -ENOTSUP; + } + if (!(can->ESR & CAN_ESR_BOFF)) { return 0; } @@ -729,8 +752,7 @@ static int can_stm32_recover(const struct device *dev, k_timeout_t timeout) k_mutex_unlock(&data->inst_mutex); return ret; } -#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ - +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ static int can_stm32_send(const struct device *dev, const struct can_frame *frame, k_timeout_t timeout, can_tx_callback_t callback, @@ -1053,9 +1075,9 @@ static const struct can_driver_api can_api_funcs = { .add_rx_filter = can_stm32_add_rx_filter, .remove_rx_filter = can_stm32_remove_rx_filter, .get_state = can_stm32_get_state, -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE .recover = can_stm32_recover, -#endif +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ .set_state_change_callback = can_stm32_set_state_change_callback, .get_core_clock = can_stm32_get_core_clock, .get_max_filters = can_stm32_get_max_filters, diff --git a/drivers/can/can_stm32_fdcan.c b/drivers/can/can_stm32_fdcan.c index fffd291778df..5eb3210eb312 100644 --- a/drivers/can/can_stm32_fdcan.c +++ b/drivers/can/can_stm32_fdcan.c @@ -586,9 +586,9 @@ static const struct can_driver_api can_stm32fd_driver_api = { .add_rx_filter = can_mcan_add_rx_filter, .remove_rx_filter = can_mcan_remove_rx_filter, .get_state = can_mcan_get_state, -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE .recover = can_mcan_recover, -#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ .get_core_clock = can_stm32fd_get_core_clock, .get_max_filters = can_mcan_get_max_filters, .set_state_change_callback = can_mcan_set_state_change_callback, diff --git a/drivers/can/can_stm32h7_fdcan.c b/drivers/can/can_stm32h7_fdcan.c index e4965f32a929..4bf917a5edef 100644 --- a/drivers/can/can_stm32h7_fdcan.c +++ b/drivers/can/can_stm32h7_fdcan.c @@ -204,9 +204,9 @@ static const struct can_driver_api can_stm32h7_driver_api = { .add_rx_filter = can_mcan_add_rx_filter, .remove_rx_filter = can_mcan_remove_rx_filter, .get_state = can_mcan_get_state, -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE .recover = can_mcan_recover, -#endif +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE*/ .get_core_clock = can_stm32h7_get_core_clock, .get_max_filters = can_mcan_get_max_filters, .set_state_change_callback = can_mcan_set_state_change_callback, diff --git a/drivers/can/can_tcan4x5x.c b/drivers/can/can_tcan4x5x.c index 8d8a1a612ad3..a68ea5e22a48 100644 --- a/drivers/can/can_tcan4x5x.c +++ b/drivers/can/can_tcan4x5x.c @@ -722,9 +722,9 @@ static const struct can_driver_api tcan4x5x_driver_api = { .send = can_mcan_send, .add_rx_filter = can_mcan_add_rx_filter, .remove_rx_filter = can_mcan_remove_rx_filter, -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE .recover = can_mcan_recover, -#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ .get_state = can_mcan_get_state, .set_state_change_callback = can_mcan_set_state_change_callback, .get_core_clock = tcan4x5x_get_core_clock, diff --git a/drivers/can/can_xmc4xxx.c b/drivers/can/can_xmc4xxx.c index b2b96bf2b3ce..3dc0bb9380c3 100644 --- a/drivers/can/can_xmc4xxx.c +++ b/drivers/can/can_xmc4xxx.c @@ -514,21 +514,6 @@ static int can_xmc4xxx_get_max_filters(const struct device *dev, bool ide) return CONFIG_CAN_MAX_FILTER; } -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY -static int can_xmc4xxx_recover(const struct device *dev, k_timeout_t timeout) -{ - struct can_xmc4xxx_data *dev_data = dev->data; - - ARG_UNUSED(timeout); - - if (!dev_data->common.started) { - return -ENETDOWN; - } - - return -ENOTSUP; -} -#endif - static void can_xmc4xxx_reset_tx_fifos(const struct device *dev, int status) { struct can_xmc4xxx_data *dev_data = dev->data; @@ -908,9 +893,6 @@ static const struct can_driver_api can_xmc4xxx_api_funcs = { .send = can_xmc4xxx_send, .add_rx_filter = can_xmc4xxx_add_rx_filter, .remove_rx_filter = can_xmc4xxx_remove_rx_filter, -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY - .recover = can_xmc4xxx_recover, -#endif .get_state = can_xmc4xxx_get_state, .set_state_change_callback = can_xmc4xxx_set_state_change_callback, .get_core_clock = can_xmc4xxx_get_core_clock, diff --git a/include/zephyr/drivers/can.h b/include/zephyr/drivers/can.h index 59be684fd385..a4556baedf18 100644 --- a/include/zephyr/drivers/can.h +++ b/include/zephyr/drivers/can.h @@ -89,19 +89,22 @@ extern "C" { #define CAN_MODE_NORMAL 0 /** Controller is in loopback mode (receives own frames). */ -#define CAN_MODE_LOOPBACK BIT(0) +#define CAN_MODE_LOOPBACK BIT(0) /** Controller is not allowed to send dominant bits. */ -#define CAN_MODE_LISTENONLY BIT(1) +#define CAN_MODE_LISTENONLY BIT(1) /** Controller allows transmitting/receiving CAN FD frames. */ -#define CAN_MODE_FD BIT(2) +#define CAN_MODE_FD BIT(2) /** Controller does not retransmit in case of lost arbitration or missing ACK */ -#define CAN_MODE_ONE_SHOT BIT(3) +#define CAN_MODE_ONE_SHOT BIT(3) /** Controller uses triple sampling mode */ -#define CAN_MODE_3_SAMPLES BIT(4) +#define CAN_MODE_3_SAMPLES BIT(4) + +/** Controller requires manual recovery after entering bus-off state */ +#define CAN_MODE_MANUAL_RECOVERY BIT(5) /** @} */ @@ -450,7 +453,7 @@ typedef int (*can_add_rx_filter_t)(const struct device *dev, typedef void (*can_remove_rx_filter_t)(const struct device *dev, int filter_id); /** - * @brief Callback API upon recovering the CAN bus + * @brief Optional callback API upon manually recovering the CAN controller from bus-off state * See @a can_recover() for argument description */ typedef int (*can_recover_t)(const struct device *dev, k_timeout_t timeout); @@ -491,9 +494,9 @@ __subsystem struct can_driver_api { can_send_t send; can_add_rx_filter_t add_rx_filter; can_remove_rx_filter_t remove_rx_filter; -#if !defined(CONFIG_CAN_AUTO_BUS_OFF_RECOVERY) || defined(__DOXYGEN__) +#if defined(CONFIG_CAN_MANUAL_RECOVERY_MODE) || defined(__DOXYGEN__) can_recover_t recover; -#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ can_get_state_t get_state; can_set_state_change_callback_t set_state_change_callback; can_get_core_clock_t get_core_clock; @@ -1413,34 +1416,32 @@ static inline int z_impl_can_get_state(const struct device *dev, enum can_state * * Recover the CAN controller from bus-off state to error-active state. * - * @note @kconfig{CONFIG_CAN_AUTO_BUS_OFF_RECOVERY} must be deselected for this + * @note @kconfig{CONFIG_CAN_MANUAL_RECOVERY_MODE} must be enabled for this * function to be available. * * @param dev Pointer to the device structure for the driver instance. * @param timeout Timeout for waiting for the recovery or ``K_FOREVER``. * * @retval 0 on success. + * @retval -ENOTSUP if the CAN controller is not in manual recovery mode. * @retval -ENETDOWN if the CAN controller is in stopped state. * @retval -EAGAIN on timeout. + * @retval -ENOSYS If this function is not implemented by the driver. */ -#if !defined(CONFIG_CAN_AUTO_BUS_OFF_RECOVERY) || defined(__DOXYGEN__) __syscall int can_recover(const struct device *dev, k_timeout_t timeout); +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE static inline int z_impl_can_recover(const struct device *dev, k_timeout_t timeout) { const struct can_driver_api *api = (const struct can_driver_api *)dev->api; + if (api->recover == NULL) { + return -ENOSYS; + } + return api->recover(dev, timeout); } -#else /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ -/* This implementation prevents inking errors for auto recovery */ -static inline int z_impl_can_recover(const struct device *dev, k_timeout_t timeout) -{ - ARG_UNUSED(dev); - ARG_UNUSED(timeout); - return 0; -} -#endif /* !CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ /** * @brief Set a callback for CAN controller state change events diff --git a/include/zephyr/drivers/can/can_mcan.h b/include/zephyr/drivers/can/can_mcan.h index 4df3754a2af8..ca7190412c27 100644 --- a/include/zephyr/drivers/can/can_mcan.h +++ b/include/zephyr/drivers/can/can_mcan.h @@ -1653,13 +1653,13 @@ int can_mcan_set_timing(const struct device *dev, const struct can_timing *timin */ int can_mcan_set_timing_data(const struct device *dev, const struct can_timing *timing_data); -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE /** * @brief Bosch M_CAN driver callback API upon recovering the CAN bus * See @a can_recover() for argument description */ int can_mcan_recover(const struct device *dev, k_timeout_t timeout); -#endif /* !CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ int can_mcan_send(const struct device *dev, const struct can_frame *frame, k_timeout_t timeout, can_tx_callback_t callback, void *user_data); diff --git a/include/zephyr/drivers/can/can_sja1000.h b/include/zephyr/drivers/can/can_sja1000.h index b4a3c5e4da22..de11d69031dc 100644 --- a/include/zephyr/drivers/can/can_sja1000.h +++ b/include/zephyr/drivers/can/can_sja1000.h @@ -227,13 +227,13 @@ int can_sja1000_add_rx_filter(const struct device *dev, can_rx_callback_t callba */ void can_sja1000_remove_rx_filter(const struct device *dev, int filter_id); -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE /** * @brief SJA1000 callback API upon recovering the CAN bus * See @a can_recover() for argument description */ int can_sja1000_recover(const struct device *dev, k_timeout_t timeout); -#endif /* !CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ +#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */ /** * @brief SJA1000 callback API upon getting the CAN controller state diff --git a/samples/drivers/can/counter/src/main.c b/samples/drivers/can/counter/src/main.c index 2119069e43ef..2fa0688eb58a 100644 --- a/samples/drivers/can/counter/src/main.c +++ b/samples/drivers/can/counter/src/main.c @@ -173,16 +173,6 @@ void state_change_work_handler(struct k_work *work) "tx error count: %d\n", state_to_str(current_state), current_err_cnt.rx_err_cnt, current_err_cnt.tx_err_cnt); - -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY - if (current_state == CAN_STATE_BUS_OFF) { - printf("Recover from bus-off\n"); - - if (can_recover(can_dev, K_MSEC(100)) != 0) { - printf("Recovery timed out\n"); - } - } -#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ } void state_change_callback(const struct device *dev, enum can_state state, diff --git a/tests/drivers/build_all/can/prj.conf b/tests/drivers/build_all/can/prj.conf index 4f651d30e268..5b33c40c8cc9 100644 --- a/tests/drivers/build_all/can/prj.conf +++ b/tests/drivers/build_all/can/prj.conf @@ -3,4 +3,4 @@ CONFIG_TEST_USERSPACE=y CONFIG_GPIO=y CONFIG_CAN=y CONFIG_CAN_FD_MODE=y -CONFIG_CAN_AUTO_BUS_OFF_RECOVERY=n +CONFIG_CAN_MANUAL_RECOVERY_MODE=y diff --git a/tests/drivers/can/api/prj.conf b/tests/drivers/can/api/prj.conf index a163dc5077fe..01dacc0dbabf 100644 --- a/tests/drivers/can/api/prj.conf +++ b/tests/drivers/can/api/prj.conf @@ -1,6 +1,6 @@ CONFIG_CAN=y CONFIG_CAN_FD_MODE=y -CONFIG_CAN_AUTO_BUS_OFF_RECOVERY=n +CONFIG_CAN_MANUAL_RECOVERY_MODE=y CONFIG_STATS=y CONFIG_CAN_STATS=y CONFIG_TEST_USERSPACE=y diff --git a/tests/drivers/can/api/src/classic.c b/tests/drivers/can/api/src/classic.c index 4a871ebd9aa5..b34bd7d98fc2 100644 --- a/tests/drivers/can/api/src/classic.c +++ b/tests/drivers/can/api/src/classic.c @@ -853,18 +853,55 @@ ZTEST_USER(can_classic, test_send_fd_format) /** * @brief Test CAN controller bus recovery. + * + * It is not possible to provoke a bus off state, but verify the API call return codes. */ ZTEST_USER(can_classic, test_recover) { + can_mode_t cap; int err; - /* It is not possible to provoke a bus off state, but test the API call */ - err = can_recover(can_dev, TEST_RECOVER_TIMEOUT); - if (err == -ENOTSUP) { - ztest_test_skip(); + Z_TEST_SKIP_IFNDEF(CONFIG_CAN_MANUAL_RECOVERY_MODE); + + err = can_get_capabilities(can_dev, &cap); + zassert_equal(err, 0, "failed to get CAN capabilities (err %d)", err); + + if ((cap & CAN_MODE_MANUAL_RECOVERY) != 0U) { + /* Check that manual recovery fails when not in manual recovery mode */ + err = can_recover(can_dev, TEST_RECOVER_TIMEOUT); + zassert_equal(err, -ENOTSUP, "wrong error return code (err %d)", err); + + err = can_stop(can_dev); + zassert_equal(err, 0, "failed to stop CAN controller (err %d)", err); + + /* Enter manual recovery mode */ + err = can_set_mode(can_dev, CAN_MODE_NORMAL | CAN_MODE_MANUAL_RECOVERY); + zassert_equal(err, 0, "failed to set manual recovery mode (err %d)", err); + zassert_equal(CAN_MODE_NORMAL | CAN_MODE_MANUAL_RECOVERY, can_get_mode(can_dev)); + + err = can_start(can_dev); + zassert_equal(err, 0, "failed to start CAN controller (err %d)", err); } - zassert_equal(err, 0, "failed to recover (err %d)", err); + err = can_recover(can_dev, TEST_RECOVER_TIMEOUT); + + if ((cap & CAN_MODE_MANUAL_RECOVERY) != 0U) { + zassert_equal(err, 0, "failed to recover (err %d)", err); + + err = can_stop(can_dev); + zassert_equal(err, 0, "failed to stop CAN controller (err %d)", err); + + /* Restore loopback mode */ + err = can_set_mode(can_dev, CAN_MODE_LOOPBACK); + zassert_equal(err, 0, "failed to set loopback-mode (err %d)", err); + zassert_equal(CAN_MODE_LOOPBACK, can_get_mode(can_dev)); + + err = can_start(can_dev); + zassert_equal(err, 0, "failed to start CAN controller (err %d)", err); + } else { + /* Check that manual recovery fails when not supported */ + zassert_equal(err, -ENOSYS, "wrong error return code (err %d)", err); + } } /** @@ -1036,8 +1073,18 @@ ZTEST_USER(can_classic, test_start_while_started) */ ZTEST_USER(can_classic, test_recover_while_stopped) { + can_mode_t cap; int err; + Z_TEST_SKIP_IFNDEF(CONFIG_CAN_MANUAL_RECOVERY_MODE); + + err = can_get_capabilities(can_dev, &cap); + zassert_equal(err, 0, "failed to get CAN capabilities (err %d)", err); + + if ((cap & CAN_MODE_MANUAL_RECOVERY) == 0U) { + ztest_test_skip(); + } + err = can_stop(can_dev); zassert_equal(err, 0, "failed to stop CAN controller (err %d)", err); diff --git a/tests/drivers/can/shell/prj.conf b/tests/drivers/can/shell/prj.conf index 15c6eafdff7a..0964517c154f 100644 --- a/tests/drivers/can/shell/prj.conf +++ b/tests/drivers/can/shell/prj.conf @@ -2,7 +2,7 @@ CONFIG_SHELL=y CONFIG_SHELL_BACKEND_SERIAL=n CONFIG_SHELL_BACKEND_DUMMY=y CONFIG_CAN=y -CONFIG_CAN_AUTO_BUS_OFF_RECOVERY=n +CONFIG_CAN_MANUAL_RECOVERY_MODE=y CONFIG_CAN_FD_MODE=y CONFIG_CAN_SHELL=y CONFIG_ZTEST=y diff --git a/tests/drivers/can/shell/src/main.c b/tests/drivers/can/shell/src/main.c index bcd54bc36d7a..227606fcb839 100644 --- a/tests/drivers/can/shell/src/main.c +++ b/tests/drivers/can/shell/src/main.c @@ -572,7 +572,7 @@ static void can_shell_test_recover(const char *cmd, k_timeout_t expected) const struct shell *sh = shell_backend_dummy_get_ptr(); int err; - Z_TEST_SKIP_IFDEF(CONFIG_CAN_AUTO_BUS_OFF_RECOVERY); + Z_TEST_SKIP_IFNDEF(CONFIG_CAN_MANUAL_RECOVERY_MODE); err = shell_execute_cmd(sh, cmd); zassert_ok(err, "failed to execute shell command (err %d)", err); From 9426740f681c901d2a09e9167c4f15a4577fcc36 Mon Sep 17 00:00:00 2001 From: Henrik Brix Andersen Date: Wed, 28 Feb 2024 22:48:43 +0100 Subject: [PATCH 2/2] docs: release: migration-guide: 3.7: list CAN bus-off recovery changes List the changes to the CAN bus-off recovery functionality. Signed-off-by: Henrik Brix Andersen --- doc/releases/migration-guide-3.7.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/doc/releases/migration-guide-3.7.rst b/doc/releases/migration-guide-3.7.rst index f2867bf5a490..64763e03d7c9 100644 --- a/doc/releases/migration-guide-3.7.rst +++ b/doc/releases/migration-guide-3.7.rst @@ -58,6 +58,24 @@ Controller Area Network (CAN) * ``phase-seg1-data`` * ``phase-seg1-data`` +* Support for manual bus-off recovery was reworked: + + * Automatic bus recovery will always be enabled upon driver initialization regardless of Kconfig + options. Since CAN controllers are initialized in "stopped" state, no unwanted bus-off recovery + will be started at this point. + * The Kconfig ``CONFIG_CAN_AUTO_BUS_OFF_RECOVERY`` was renamed (and inverted) to + :kconfig:option:`CONFIG_CAN_MANUAL_RECOVERY_MODE`, which is disabled by default. This Kconfig + option enables support for the :c:func:`can_recover()` API function and a new manual recovery mode + (see the next bullet). + * A new CAN controller operational mode :c:macro:`CAN_MODE_MANUAL_RECOVERY` was added. Support for + this is only enabled if :kconfig:option:`CONFIG_CAN_MANUAL_RECOVERY_MODE` is enabled. Having + this as a mode allows applications to inquire whether the CAN controller supports manual + recovery mode via the :c:func:`can_get_capabilities` API function. The application can then + either fail initialization or rely on automatic bus-off recovery. Having this as a mode + furthermore allows CAN controller drivers not supporting manual recovery mode to fail early in + :c:func:`can_set_mode` during application startup instead of failing when :c:func:`can_recover` + is called at a later point in time. + Display =======