diff --git a/boards/nxp/frdm_mcxa153/frdm_mcxa153.dts b/boards/nxp/frdm_mcxa153/frdm_mcxa153.dts index 2c626ef183d5a..299e324f22670 100644 --- a/boards/nxp/frdm_mcxa153/frdm_mcxa153.dts +++ b/boards/nxp/frdm_mcxa153/frdm_mcxa153.dts @@ -35,6 +35,7 @@ zephyr,code-partition = &slot0_partition; zephyr,console = &lpuart0; zephyr,shell-uart = &lpuart0; + zephyr,edac = &erm0; }; leds { @@ -129,6 +130,18 @@ status = "okay"; }; +&eim0 { + status = "okay"; +}; + +&eim0_channel0 { + status = "okay"; +}; + +&erm0 { + status = "okay"; +}; + &edma0 { status = "okay"; }; diff --git a/boards/nxp/frdm_mcxe247/frdm_mcxe247.dts b/boards/nxp/frdm_mcxe247/frdm_mcxe247.dts index b896147907ee7..7f508a22b864a 100644 --- a/boards/nxp/frdm_mcxe247/frdm_mcxe247.dts +++ b/boards/nxp/frdm_mcxe247/frdm_mcxe247.dts @@ -30,6 +30,7 @@ zephyr,console = &lpuart2; zephyr,shell-uart = &lpuart2; zephyr,canbus = &flexcan0; + zephyr,edac = &erm0; }; leds { @@ -101,6 +102,18 @@ clock-frequency = <80000000>; }; +&erm0 { + status = "okay"; +}; + +&eim0 { + status = "okay"; +}; + +&eim0_channel1 { + status = "okay"; +}; + &sosc_clk { clock-frequency = <8000000>; status = "okay"; diff --git a/boards/nxp/frdm_mcxe31b/frdm_mcxe31b.dts b/boards/nxp/frdm_mcxe31b/frdm_mcxe31b.dts index e6997b69ec57c..29477680f4933 100644 --- a/boards/nxp/frdm_mcxe31b/frdm_mcxe31b.dts +++ b/boards/nxp/frdm_mcxe31b/frdm_mcxe31b.dts @@ -32,6 +32,7 @@ zephyr,flash = &program_flash; zephyr,flash-controller = &flash; zephyr,console = &lpuart_5; + zephyr,edac = &erm0; zephyr,shell-uart = &lpuart_5; }; @@ -98,6 +99,22 @@ clock-frequency = ; }; +&erm0 { + status = "okay"; +}; + +&eim0 { + status = "okay"; +}; + +&eim0_channel14 { + status = "okay"; +}; + +&eim0_channel15 { + status = "okay"; +}; + &gpiob_h { status = "okay"; }; diff --git a/boards/nxp/frdm_mcxn236/frdm_mcxn236.dts b/boards/nxp/frdm_mcxn236/frdm_mcxn236.dts index a22cac77b6aaf..79d5e51aa2fd3 100644 --- a/boards/nxp/frdm_mcxn236/frdm_mcxn236.dts +++ b/boards/nxp/frdm_mcxn236/frdm_mcxn236.dts @@ -26,6 +26,7 @@ zephyr,console = &flexcomm4_lpuart4; zephyr,shell-uart = &flexcomm4_lpuart4; zephyr,canbus = &flexcan1; + zephyr,edac = &erm0; }; aliases { @@ -161,6 +162,30 @@ status = "okay"; }; +&eim0 { + status = "okay"; +}; + +&eim0_channel0 { + status = "okay"; +}; + +&eim0_channel2 { + status = "okay"; +}; + +&eim0_channel3 { + status = "okay"; +}; + +&eim0_channel4 { + status = "okay"; +}; + +&erm0 { + status = "okay"; +}; + &flexcomm2 { status = "okay"; }; diff --git a/boards/nxp/frdm_mcxn947/frdm_mcxn947.dtsi b/boards/nxp/frdm_mcxn947/frdm_mcxn947.dtsi index e4018e362a40e..8cd46a48393c8 100644 --- a/boards/nxp/frdm_mcxn947/frdm_mcxn947.dtsi +++ b/boards/nxp/frdm_mcxn947/frdm_mcxn947.dtsi @@ -21,6 +21,10 @@ mcuboot-button0 = &user_button_2; }; + chosen { + zephyr,edac = &erm0; + }; + leds { compatible = "gpio-leds"; @@ -130,6 +134,38 @@ }; }; +&eim0 { + status = "okay"; +}; + +&eim0_channel0 { + status = "okay"; +}; + +&eim0_channel2 { + status = "okay"; +}; + +&eim0_channel3 { + status = "okay"; +}; + +&eim0_channel4 { + status = "okay"; +}; + +&eim0_channel5 { + status = "okay"; +}; + +&eim0_channel6 { + status = "okay"; +}; + +&erm0 { + status = "okay"; +}; + &flexcomm1_lpspi1 { pinctrl-0 = <&pinmux_flexcomm1_lpspi>; pinctrl-names = "default"; diff --git a/drivers/edac/CMakeLists.txt b/drivers/edac/CMakeLists.txt index 0d2e6083d0432..a3993f8f8e6fb 100644 --- a/drivers/edac/CMakeLists.txt +++ b/drivers/edac/CMakeLists.txt @@ -1,4 +1,5 @@ # Copyright (c) 2020 Intel Corporation +# Copyright 2025 NXP # SPDX-License-Identifier: Apache-2.0 zephyr_library() @@ -7,5 +8,6 @@ zephyr_library_sources_ifdef(CONFIG_EDAC_SHELL shell.c) # zephyr-keep-sorted-start zephyr_library_sources_ifdef(CONFIG_EDAC_IBECC edac_ibecc.c) +zephyr_library_sources_ifdef(CONFIG_EDAC_NXP_ERM edac_mcux_erm.c) zephyr_library_sources_ifdef(CONFIG_EDAC_SYNOPSYS edac_synopsys.c) # zephyr-keep-sorted-stop diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index edd95050c20ba..760c8b13bf998 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -1,4 +1,5 @@ # Copyright (c) 2020 Intel Corp. +# Copyright 2025 NXP # SPDX-License-Identifier: Apache-2.0 # # EDAC configuration options @@ -35,6 +36,7 @@ config EDAC_SYNOPSYS help Enable the Synopsys DDR controller EDAC driver. +source "drivers/edac/Kconfig.mcux_erm" module = EDAC module-str = edac source "subsys/logging/Kconfig.template.log_config" diff --git a/drivers/edac/Kconfig.mcux_erm b/drivers/edac/Kconfig.mcux_erm new file mode 100644 index 0000000000000..c472cf13435e3 --- /dev/null +++ b/drivers/edac/Kconfig.mcux_erm @@ -0,0 +1,38 @@ +# Copyright 2025 NXP +# +# SPDX-License-Identifier: Apache-2.0 + +config EDAC_NXP_ERM + bool "NXP ERM driver" + default y + depends on DT_HAS_NXP_ERM_ENABLED + help + Enable the NXP Error Reporting Module driver. + +config EDAC_NXP_ERM_DEFAULT_CHANNEL + int "NXP ERM default channel" + default 0 + depends on EDAC_NXP_ERM + help + Set the NXP Error Reporting Module default channel. + +config EDAC_NXP_EIM + bool "NXP EIM driver" + default y + depends on DT_HAS_NXP_EIM_ENABLED + help + Enable the NXP Error Injection Module driver. + +config EDAC_NXP_ERROR_INJECT + bool + default y if EDAC_ERROR_INJECT && EDAC_NXP_EIM + help + This is a helper configuration that is enabled when both + EDAC_ERROR_INJECT and EDAC_NXP_EIM are enabled. + +config EDAC_NXP_ERM_VARY_WITH_EIM_CHANNEL + bool "Update EIM channel based on EIM channel" + default y + depends on EDAC_NXP_EIM && EDAC_NXP_ERM + help + Allow ERM channel to dynamically adapt based on the selected EIM channel. diff --git a/drivers/edac/edac_mcux_erm.c b/drivers/edac/edac_mcux_erm.c new file mode 100644 index 0000000000000..693bf882aefec --- /dev/null +++ b/drivers/edac/edac_mcux_erm.c @@ -0,0 +1,355 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(edac_mcux_erm, CONFIG_EDAC_LOG_LEVEL); + +#ifdef EDAC_NXP_ERROR_INJECT +#include + +#define EDAC_NXP_SINGLE_BIT_ERROR_MASK 0x1 +#define EDAC_NXP_DOUBLE_BIT_ERROR_MASK 0x3 + +#define EIM_CHANNEL_ENABLE(CHANNEL_ID) (0x80000000U >> (CHANNEL_ID)) + +struct edac_nxp_eim_channel { + uint32_t start_address; + uint32_t size; + uint32_t ecc_enable; + uint8_t channel_id; + uint8_t erm_channel_id; +}; +#endif /* CONFIG_EDAC_ERROR_INJECT */ + +struct edac_nxp_config { + ERM_Type *erm_base; +#ifdef EDAC_NXP_ERROR_INJECT + EIM_Type *eim_base; + const struct edac_nxp_eim_channel *eim_channels; + uint8_t eim_channel_num; +#endif /* CONFIG_EDAC_ERROR_INJECT */ + const int *erm_channels; + uint8_t erm_channel_num; + void (*irq_config_func)(const struct device *dev); +}; + +struct edac_nxp_data { + edac_notify_callback_f cb; +#ifdef EDAC_NXP_ERROR_INJECT + uint32_t eim_channel; + uint32_t eim_channel_word; + uint32_t inject_error_type; +#endif /* CONFIG_EDAC_ERROR_INJECT */ + uint32_t erm_channel; +}; + +__weak void enable_ecc(uint32_t mask) +{ + ARG_UNUSED(mask); +} + +static bool check_erm_channel(const int *erm_channels, size_t size, uint32_t value) +{ + for (size_t i = 0; i < size; i++) { + if (erm_channels[i] == value) { + return true; + } + } + return false; +} + +#ifdef EDAC_NXP_ERROR_INJECT +static bool check_eim_channel(const struct edac_nxp_eim_channel *eim_channels, size_t size, + uint32_t value) +{ + for (size_t i = 0; i < size; i++) { + if (eim_channels[i].channel_id == value) { + return true; + } + } + return false; +} + +static inline const struct edac_nxp_eim_channel * +get_eim_channel(const struct edac_nxp_eim_channel *eim_channels, size_t size, uint32_t value) +{ + for (size_t i = 0; i < size; i++) { + if (eim_channels[i].channel_id == value) { + return &eim_channels[i]; + } + } + return NULL; +} + +static int inject_set_param1(const struct device *dev, uint64_t channel) +{ + struct edac_nxp_data *data = dev->data; + const struct edac_nxp_config *config = dev->config; + + if (!check_eim_channel(config->eim_channels, config->eim_channel_num, (uint32_t)channel)) { + LOG_ERR("Invalid EIM channel %llx", channel); + return -EINVAL; + } + + data->eim_channel = (uint32_t)(channel & 0xFFFFFFFFU); + return 0; +} + +static int inject_get_param1(const struct device *dev, uint64_t *value) +{ + struct edac_nxp_data *data = dev->data; + + *value = (uint64_t)data->eim_channel; + return 0; +} + +static int inject_set_param2(const struct device *dev, uint64_t word) +{ + struct edac_nxp_data *data = dev->data; + + data->eim_channel_word = (uint32_t)(word & 0xFU); + return 0; +} + +static int inject_get_param2(const struct device *dev, uint64_t *word) +{ + struct edac_nxp_data *data = dev->data; + + *word = (uint64_t)data->eim_channel_word; + return 0; +} + +static int inject_set_error_type(const struct device *dev, uint32_t inject_error_type) +{ + struct edac_nxp_data *data = dev->data; + + data->inject_error_type = inject_error_type; + return 0; +} + +static int inject_get_error_type(const struct device *dev, uint32_t *inject_error_type) +{ + struct edac_nxp_data *data = dev->data; + + *inject_error_type = data->inject_error_type; + return 0; +} + +static int inject_error_trigger(const struct device *dev) +{ + const struct edac_nxp_config *config = dev->config; + struct edac_nxp_data *data = dev->data; + uint32_t inject_data; + const struct edac_nxp_eim_channel *eim_channel_data = + get_eim_channel(config->eim_channels, config->eim_channel_num, data->eim_channel); + + switch (data->inject_error_type) { + case EDAC_ERROR_TYPE_DRAM_COR: + inject_data = EDAC_NXP_SINGLE_BIT_ERROR_MASK; + break; + case EDAC_ERROR_TYPE_DRAM_UC: + inject_data = EDAC_NXP_DOUBLE_BIT_ERROR_MASK; + break; + default: + LOG_ERR("No error type found."); + return -EINVAL; + } + +#if defined(CONFIG_EDAC_NXP_ERM_VARY_WITH_EIM_CHANNEL) && CONFIG_EDAC_NXP_ERM_VARY_WITH_EIM_CHANNEL + if (!check_erm_channel(config->erm_channels, config->erm_channel_num, + eim_channel_data->erm_channel_id)) { + LOG_WRN("Invalid ERM channel %d", eim_channel_data->erm_channel_id); + } else { + LOG_DBG("Setting ERM channel %d for error reporting", + eim_channel_data->erm_channel_id); + data->erm_channel = eim_channel_data->erm_channel_id; + } +#endif + + if (eim_channel_data->ecc_enable) { + enable_ecc(eim_channel_data->ecc_enable); + } + + if (data->eim_channel_word == 0U) { + EIM_InjectCheckBitError(config->eim_base, data->eim_channel, inject_data); + } else { + EIM_InjectDataWordBitError(config->eim_base, data->eim_channel, inject_data, + data->eim_channel_word); + } + + EIM_EnableErrorInjectionChannels(config->eim_base, EIM_CHANNEL_ENABLE(data->eim_channel)); + ERM_EnableInterrupts(config->erm_base, data->erm_channel, kERM_AllInterruptsEnable); + LOG_INF("EIM channel %d, range 0x%x - 0x%x ECC error injection triggered.", + data->eim_channel, eim_channel_data->start_address, + eim_channel_data->start_address + eim_channel_data->size - 1); + return 0; +} +#endif /* CONFIG_EDAC_ERROR_INJECT */ + +static int errors_cor_get(const struct device *dev) +{ +#if defined(ERM_CORR_ERR_CNT0_COUNT_MASK) && ERM_CORR_ERR_CNT0_COUNT_MASK + const struct edac_nxp_config *config = dev->config; + struct edac_nxp_data *data = dev->data; + + return (int)ERM_GetErrorCount(config->erm_base, data->erm_channel); +#else + return -ENOSYS; +#endif +} + +static int notify_callback_set(const struct device *dev, edac_notify_callback_f cb) +{ + struct edac_nxp_data *data = dev->data; + unsigned int key = irq_lock(); + + data->cb = cb; + irq_unlock(key); + return 0; +} + +static void edac_nxp_isr(const struct device *dev) +{ + const struct edac_nxp_config *config = dev->config; + struct edac_nxp_data *data = dev->data; + uint32_t status = ERM_GetInterruptStatus(config->erm_base, data->erm_channel); +#if defined(ERM_SYN0_SYNDROME_MASK) && ERM_SYN0_SYNDROME_MASK + uint32_t syndrome = ERM_GetSyndrome(config->erm_base, data->erm_channel); +#else + uint32_t syndrome = -ENOSYS; +#endif + struct edac_nxp_callback_data cb_data = { + .corr_err_count = errors_cor_get(dev), + .err_syndrome = syndrome, + .err_addr = ERM_GetMemoryErrorAddr(config->erm_base, data->erm_channel), + .err_status = status, + }; + + if (kERM_SingleBitCorrectionIntFlag == (status & kERM_SingleBitCorrectionIntFlag)) { + LOG_ERR("ERM channel %d correctable ECC error detected, address/offset 0x%x, " + "syndrome " + "0x%02x, correctable ECC count %d", + data->erm_channel, cb_data.err_addr, cb_data.err_syndrome, + cb_data.corr_err_count); + ERM_ClearInterruptStatus(config->erm_base, data->erm_channel, + kERM_SingleBitCorrectionIntFlag); + } else if (kERM_NonCorrectableErrorIntFlag == (status & kERM_NonCorrectableErrorIntFlag)) { + LOG_ERR("ERM channel %d uncorrectable ECC error detected, address/offset 0x%x", + data->erm_channel, cb_data.err_addr); + ERM_ClearInterruptStatus(config->erm_base, data->erm_channel, + kERM_NonCorrectableErrorIntFlag); + } else { + LOG_ERR("ERM unknown ECC error status detected, it may caused by unaligned ERM " + "channel"); + ERM_ClearInterruptStatus(config->erm_base, data->erm_channel, kERM_AllIntsFlag); + } + if (data->cb) { + data->cb(dev, &cb_data); + } +} + +static DEVICE_API(edac, edac_nxp_api) = { +#ifdef EDAC_NXP_ERROR_INJECT + /* Error Injection functions */ + .inject_set_param1 = inject_set_param1, + .inject_get_param1 = inject_get_param1, + .inject_set_param2 = inject_set_param2, + .inject_get_param2 = inject_get_param2, + .inject_set_error_type = inject_set_error_type, + .inject_get_error_type = inject_get_error_type, + .inject_error_trigger = inject_error_trigger, +#endif /* CONFIG_EDAC_ERROR_INJECT */ + + /* Get error stats */ + .errors_cor_get = errors_cor_get, + + /* Notification callback set */ + .notify_cb_set = notify_callback_set, +}; + +static int edac_nxp_init(const struct device *dev) +{ + const struct edac_nxp_config *config = dev->config; + struct edac_nxp_data *data = dev->data; + +#ifdef EDAC_NXP_ERROR_INJECT + EIM_Init(config->eim_base); + EIM_EnableGlobalErrorInjection(config->eim_base, true); + data->eim_channel_word = 1U; + LOG_INF("EIM driver initialized"); +#endif /* CONFIG_EDAC_ERROR_INJECT */ + + ERM_Init(config->erm_base); + if (!check_erm_channel(config->erm_channels, config->erm_channel_num, + CONFIG_EDAC_NXP_ERM_DEFAULT_CHANNEL)) { + LOG_ERR("Invalid ERM channel %d", CONFIG_EDAC_NXP_ERM_DEFAULT_CHANNEL); + return -EINVAL; + } + data->erm_channel = CONFIG_EDAC_NXP_ERM_DEFAULT_CHANNEL; + + /* Clear any latched status before enabling interrupts */ + ERM_ClearInterruptStatus(config->erm_base, data->erm_channel, kERM_AllIntsFlag); + config->irq_config_func(dev); + ERM_EnableInterrupts(config->erm_base, data->erm_channel, kERM_AllInterruptsEnable); + LOG_INF("ERM driver initialized"); + + return 0; +} + +#define DT_DRV_COMPAT nxp_erm + +#ifdef EDAC_NXP_ERROR_INJECT +/* Initializes an element of the eim channel device pointer array */ +#define NXP_EIM_CHANNEL_DEV_ARRAY_INIT(node) \ + { \ + .channel_id = DT_PROP(node, channel_id), \ + .erm_channel_id = DT_PROP_OR(node, erm_channel_id, 0xFFU), \ + .start_address = DT_PROP(node, start_address), \ + .ecc_enable = DT_PROP_OR(node, ecc_enable, 0), \ + .size = DT_PROP(node, size), \ + }, +const struct edac_nxp_eim_channel edac_nxp_eim_0_channels[] = { + DT_FOREACH_CHILD_STATUS_OKAY(DT_NODELABEL(eim0), NXP_EIM_CHANNEL_DEV_ARRAY_INIT)}; + +#define EDAC_NXP_EIM_CONFIG_FIELDS \ + .eim_base = (EIM_Type *)DT_REG_ADDR(DT_NODELABEL(eim0)), \ + .eim_channels = edac_nxp_eim_0_channels, \ + .eim_channel_num = ARRAY_SIZE(edac_nxp_eim_0_channels), +#else +#define EDAC_NXP_EIM_CHANNELS +#define EDAC_NXP_EIM_CONFIG_FIELDS +#endif + +static const int edac_nxp_erm_0_channels[] = DT_INST_PROP(0, channels); + +static void edac_nxp_irq_0(const struct device *dev) +{ + IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, 0, irq), DT_INST_IRQ_BY_IDX(0, 0, priority), edac_nxp_isr, + DEVICE_DT_INST_GET(0), 0); + irq_enable(DT_INST_IRQ_BY_IDX(0, 0, irq)); + IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, 1, irq), DT_INST_IRQ_BY_IDX(0, 1, priority), edac_nxp_isr, + DEVICE_DT_INST_GET(0), 0); + irq_enable(DT_INST_IRQ_BY_IDX(0, 1, irq)); +} + +static const struct edac_nxp_config edac_nxp_config_0 = { + .erm_base = (ERM_Type *)DT_INST_REG_ADDR(0), + EDAC_NXP_EIM_CONFIG_FIELDS.erm_channels = edac_nxp_erm_0_channels, + .erm_channel_num = ARRAY_SIZE(edac_nxp_erm_0_channels), + .irq_config_func = edac_nxp_irq_0, +}; + +static struct edac_nxp_data edac_nxp_data_0; + +DEVICE_DT_INST_DEFINE(0, &edac_nxp_init, NULL, &edac_nxp_data_0, &edac_nxp_config_0, POST_KERNEL, + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &edac_nxp_api); diff --git a/dts/arm/nxp/nxp_mcxa153.dtsi b/dts/arm/nxp/nxp_mcxa153.dtsi index cf804c79cce2d..a89df273bb29a 100644 --- a/dts/arm/nxp/nxp_mcxa153.dtsi +++ b/dts/arm/nxp/nxp_mcxa153.dtsi @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -84,6 +85,31 @@ status = "disabled"; }; + eim0: eim@4008c000 { + compatible = "nxp,eim"; + status = "disabled"; + reg = <0x4008c000 0x1000>; + resets = <&reset NXP_SYSCON_RESET(0, 11)>; + + eim0_channel0: channel0 { + channel-id = <0>; + erm-channel-id = <0>; + size = ; + start-address = <0x20000000>; + ecc-enable = <0x1>; + status = "disabled"; + }; + }; + + erm0: erm@4008d000 { + compatible = "nxp,erm"; + status = "disabled"; + reg = <0x4008d000 0x1000>; + resets = <&reset NXP_SYSCON_RESET(0, 12)>; + interrupts = <10 0>, <11 0>; + channels = <0 1>; + }; + flexpwm0: flexpwm@400a9000 { compatible = "nxp,flexpwm"; reg = <0x400a9000 0x1000>; diff --git a/dts/arm/nxp/nxp_mcxe245.dtsi b/dts/arm/nxp/nxp_mcxe245.dtsi index d17ea02eba7ce..9333bbf2c3715 100644 --- a/dts/arm/nxp/nxp_mcxe245.dtsi +++ b/dts/arm/nxp/nxp_mcxe245.dtsi @@ -26,6 +26,16 @@ }; }; +&eim0_channel0 { + start-address = <0x1fff8000>; + size = ; +}; + +&eim0_channel1 { + start-address = <0x20000000>; + size = ; +}; + &ftfc { flash0: flash@0 { compatible = "soc-nv-flash"; diff --git a/dts/arm/nxp/nxp_mcxe246.dtsi b/dts/arm/nxp/nxp_mcxe246.dtsi index 342e5384a89fd..dae789cfb19f1 100644 --- a/dts/arm/nxp/nxp_mcxe246.dtsi +++ b/dts/arm/nxp/nxp_mcxe246.dtsi @@ -26,6 +26,16 @@ }; }; +&eim0_channel0 { + start-address = <0x1fff0000>; + size = ; +}; + +&eim0_channel1 { + start-address = <0x20000000>; + size = ; +}; + &ftfc { flash0: flash@0 { compatible = "soc-nv-flash"; diff --git a/dts/arm/nxp/nxp_mcxe247.dtsi b/dts/arm/nxp/nxp_mcxe247.dtsi index fbeb9a5f69734..33824b7876728 100644 --- a/dts/arm/nxp/nxp_mcxe247.dtsi +++ b/dts/arm/nxp/nxp_mcxe247.dtsi @@ -26,6 +26,16 @@ }; }; +&eim0_channel0 { + start-address = <0x1ffe0000>; + size = ; +}; + +&eim0_channel1 { + start-address = <0x20000000>; + size = ; +}; + &ftfc { flash0: flash@0 { compatible = "soc-nv-flash"; diff --git a/dts/arm/nxp/nxp_mcxe24x_common.dtsi b/dts/arm/nxp/nxp_mcxe24x_common.dtsi index 6e80e548c97d3..5573232511d74 100644 --- a/dts/arm/nxp/nxp_mcxe24x_common.dtsi +++ b/dts/arm/nxp/nxp_mcxe24x_common.dtsi @@ -218,6 +218,32 @@ status = "disabled"; }; + erm0: erm@40018000 { + compatible = "nxp,erm"; + reg = <0x40018000 0x114>; + interrupts = <44 0>, <45 0>; + channels = <0 1>; + status = "disabled"; + }; + + eim0: eim@40019000 { + compatible = "nxp,eim"; + reg = <0x40019000 0x208>; + status = "disabled"; + + eim0_channel0: channel0 { + channel-id = <0>; + erm-channel-id = <0>; + status = "disabled"; + }; + + eim0_channel1: channel1 { + channel-id = <1>; + erm-channel-id = <1>; + status = "disabled"; + }; + }; + enet: ethernet@40079000 { compatible = "nxp,enet"; reg = <0x40079000 0x620>; @@ -261,8 +287,8 @@ compatible = "nxp,flexcan-fd", "nxp,flexcan"; reg = <0x40024000 0x1000>; interrupts = <78 0>, <79 0>, <80 0>, <81 0>, <82 0>; - interrupt-names = "ored-warning-bus-off", "error", "wake-up", - "mb-0-15", "mb-16-31"; + interrupt-names = "ored-warning-bus-off", "error", + "wake-up", "mb-0-15", "mb-16-31"; clocks = <&scg KINETIS_SCG_CORESYS_CLK>; clk-source = <0>; status = "disabled"; diff --git a/dts/arm/nxp/nxp_mcxe31b.dtsi b/dts/arm/nxp/nxp_mcxe31b.dtsi index ba7fec4f3b4d0..586f7cd33e428 100644 --- a/dts/arm/nxp/nxp_mcxe31b.dtsi +++ b/dts/arm/nxp/nxp_mcxe31b.dtsi @@ -52,3 +52,18 @@ }; #include + +&eim0_channel0 { + start-address = <0x20408000>; + size = ; +}; + +&eim0_channel14 { + start-address = <0x20008000>; + size = ; +}; + +&eim0_channel15 { + start-address = <0x20010000>; + size = ; +}; diff --git a/dts/arm/nxp/nxp_mcxe31x_common.dtsi b/dts/arm/nxp/nxp_mcxe31x_common.dtsi index 3a914b9788170..5581bf87998b5 100644 --- a/dts/arm/nxp/nxp_mcxe31x_common.dtsi +++ b/dts/arm/nxp/nxp_mcxe31x_common.dtsi @@ -152,10 +152,94 @@ status = "disabled"; }; - eim: eim@258000 { + eim0: eim@258000 { compatible = "nxp,eim"; reg = <0x258000 0x8a4>; status = "disabled"; + + eim0_channel0: channel0 { + channel-id = <0>; + erm-channel-id = <0>; + status = "disabled"; + }; + + eim0_channel1: channel1 { + channel-id = <1>; + erm-channel-id = <1>; + status = "disabled"; + }; + + eim0_channel2: channel2 { + channel-id = <2>; + erm-channel-id = <16>; + status = "disabled"; + }; + + eim0_channel3: channel3 { + channel-id = <3>; + erm-channel-id = <2>; + status = "disabled"; + }; + + eim0_channel4: channel4 { + channel-id = <4>; + erm-channel-id = <3>; + status = "disabled"; + }; + + eim0_channel5: channel5 { + channel-id = <5>; + erm-channel-id = <4>; + status = "disabled"; + }; + + eim0_channel6: channel6 { + channel-id = <6>; + erm-channel-id = <5>; + status = "disabled"; + }; + + eim0_channel7: channel7 { + channel-id = <7>; + erm-channel-id = <5>; + status = "disabled"; + }; + + eim0_channel13: channel13 { + channel-id = <13>; + erm-channel-id = <10>; + status = "disabled"; + }; + + eim0_channel14: channel14 { + channel-id = <14>; + erm-channel-id = <11>; + status = "disabled"; + }; + + eim0_channel15: channel15 { + channel-id = <15>; + erm-channel-id = <12>; + status = "disabled"; + }; + + eim0_channel16: channel16 { + channel-id = <16>; + erm-channel-id = <13>; + status = "disabled"; + }; + + eim0_channel17: channel17 { + channel-id = <17>; + erm-channel-id = <14>; + status = "disabled"; + }; + + eim0_channel18: channel18 { + channel-id = <18>; + erm-channel-id = <15>; + status = "disabled"; + }; }; emac: emac@480000 { @@ -187,11 +271,12 @@ status = "disabled"; }; - erm: erm@25c000 { + erm0: erm@25c000 { compatible = "nxp,erm"; reg = <0x25c000 0x258>; interrupts = <36 0>, <37 0>; interrupt-names = "erm-0", "erm-1"; + channels = <0 1 2 3 4 5 10 11 12 13 14 15 16 17 18 19>; status = "disabled"; }; diff --git a/dts/arm/nxp/nxp_mcxn23x_common.dtsi b/dts/arm/nxp/nxp_mcxn23x_common.dtsi index 359f8c5760e1c..ff53e9b89a084 100644 --- a/dts/arm/nxp/nxp_mcxn23x_common.dtsi +++ b/dts/arm/nxp/nxp_mcxn23x_common.dtsi @@ -191,6 +191,66 @@ nxp,kinetis-port = <&portf>; }; + eim0: eim@5b000 { + compatible = "nxp,eim"; + status = "disabled"; + reg = <0x5b000 0x1000>; + resets = <&reset NXP_SYSCON_RESET(3, 24)>; + + eim0_channel0: channel0 { + channel-id = <0>; + erm-channel-id = <0>; + size = ; + start-address = <0x4000000>; + ecc-enable = <0x2>; + status = "disabled"; + }; + + eim0_channel1: channel1 { + channel-id = <1>; + erm-channel-id = <1>; + size = ; + start-address = <0x20000000>; + ecc-enable = <0x1>; + status = "disabled"; + }; + + eim0_channel2: channel2 { + channel-id = <2>; + erm-channel-id = <2>; + size = ; + start-address = <0x20008000>; + ecc-enable = <0x2>; + status = "disabled"; + }; + + eim0_channel3: channel3 { + channel-id = <3>; + erm-channel-id = <3>; + size = ; + start-address = <0x20010000>; + ecc-enable = <0x4>; + status = "disabled"; + }; + + eim0_channel4: channel4 { + channel-id = <4>; + erm-channel-id = <4>; + size = ; + start-address = <0x20020000>; + ecc-enable = <0x4>; + status = "disabled"; + }; + }; + + erm0: erm@5c000 { + compatible = "nxp,erm"; + status = "disabled"; + reg = <0x5c000 0x1000>; + interrupts = <136 0>, <137 0>; + channels = <0 1 2 3 4 7 8 9>; + }; + flexcomm0: flexcomm@92000 { compatible = "nxp,lp-flexcomm"; reg = <0x92000 0x1000>; diff --git a/dts/arm/nxp/nxp_mcxnx4x_common.dtsi b/dts/arm/nxp/nxp_mcxnx4x_common.dtsi index 0779a92ce3068..7e3ff1dc8e931 100644 --- a/dts/arm/nxp/nxp_mcxnx4x_common.dtsi +++ b/dts/arm/nxp/nxp_mcxnx4x_common.dtsi @@ -182,6 +182,84 @@ nxp,kinetis-port = <&portf>; }; + eim0: eim@5b000 { + compatible = "nxp,eim"; + status = "disabled"; + reg = <0x5b000 0x1000>; + resets = <&reset NXP_SYSCON_RESET(3, 24)>; + + eim0_channel0: channel0 { + channel-id = <0>; + erm-channel-id = <0>; + size = ; + start-address = <0x4000000>; + ecc-enable = <0x2>; + status = "disabled"; + }; + + eim0_channel1: channel1 { + channel-id = <1>; + erm-channel-id = <1>; + size = ; + start-address = <0x20000000>; + ecc-enable = <0x1>; + status = "disabled"; + }; + + eim0_channel2: channel2 { + channel-id = <2>; + erm-channel-id = <2>; + size = ; + start-address = <0x20008000>; + ecc-enable = <0x2>; + status = "disabled"; + }; + + eim0_channel3: channel3 { + channel-id = <3>; + erm-channel-id = <3>; + size = ; + start-address = <0x20010000>; + ecc-enable = <0x4>; + status = "disabled"; + }; + + eim0_channel4: channel4 { + channel-id = <4>; + erm-channel-id = <4>; + size = ; + start-address = <0x20020000>; + ecc-enable = <0x4>; + status = "disabled"; + }; + + eim0_channel5: channel5 { + channel-id = <5>; + erm-channel-id = <5>; + size = ; + start-address = <0x20030000>; + ecc-enable = <0x8>; + status = "disabled"; + }; + + eim0_channel6: channel6 { + channel-id = <6>; + erm-channel-id = <6>; + size = ; + start-address = <0x20040000>; + ecc-enable = <0x8>; + status = "disabled"; + }; + }; + + erm0: erm@5c000 { + compatible = "nxp,erm"; + status = "disabled"; + reg = <0x5c000 0x1000>; + interrupts = <136 0>, <137 0>; + channels = <0 1 2 3 4 5 6 7 8 9>; + }; + flexcomm0: flexcomm@92000 { compatible = "nxp,lp-flexcomm"; reg = <0x92000 0x1000>; diff --git a/dts/bindings/edac/nxp,eim.yaml b/dts/bindings/edac/nxp,eim.yaml new file mode 100644 index 0000000000000..47b2b52042bb9 --- /dev/null +++ b/dts/bindings/edac/nxp,eim.yaml @@ -0,0 +1,32 @@ +# Copyright 2025 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: NXP Error Injection Module + +compatible: "nxp,eim" + +include: [reset-device.yaml, base.yaml] + +child-binding: + properties: + channel-id: + required: true + type: int + + erm-channel-id: + type: int + description: erm channel id for current eim channel. + + size: + required: true + type: int + description: size of the memory channel. + + start-address: + required: true + type: int + description: start address of the memory channel. + + ecc-enable: + type: int + description: ecc enable magic number for the memory channel. diff --git a/dts/bindings/edac/nxp,erm.yaml b/dts/bindings/edac/nxp,erm.yaml new file mode 100644 index 0000000000000..d824f6b9b477e --- /dev/null +++ b/dts/bindings/edac/nxp,erm.yaml @@ -0,0 +1,16 @@ +# Copyright 2025 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: NXP Error Reporting Module + +compatible: "nxp,erm" + +include: [reset-device.yaml, base.yaml] + +properties: + interrupts: + required: true + + channels: + type: array + required: true diff --git a/include/zephyr/drivers/edac/edac_mcux_erm.h b/include/zephyr/drivers/edac/edac_mcux_erm.h new file mode 100644 index 0000000000000..67b903cccc030 --- /dev/null +++ b/include/zephyr/drivers/edac/edac_mcux_erm.h @@ -0,0 +1,37 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Header file for extended EDAC API of NXP Error Reporting Module (ERM) + * @ingroup edac_interface_ext + */ + +#ifndef ZEPHYR_DRIVERS_EDAC_NXP_ERM_H_ +#define ZEPHYR_DRIVERS_EDAC_NXP_ERM_H_ + +/** + * @brief NXP ERM EDAC driver + * @defgroup edac_nxp_erm_interface NXP ERM EDAC + * @ingroup edac_interface_ext + * @{ + */ + +/** Callback data provided to function passed to @ref edac_notify_callback_set */ +struct edac_nxp_callback_data { + /** Number of corrected errors */ + uint8_t corr_err_count; + /** Syndrome ECC bits for last single bit ECC event */ + uint8_t err_syndrome; + /** Address of last ECC event */ + uint32_t err_addr; + /** Type of last ECC event */ + uint32_t err_status; +}; + +/** @} */ + +#endif /* ZEPHYR_DRIVERS_EDAC_NXP_ERM_H_ */ diff --git a/modules/hal_nxp/mcux/mcux-sdk-ng/drivers/drivers.cmake b/modules/hal_nxp/mcux/mcux-sdk-ng/drivers/drivers.cmake index 50ab59116d862..b0036012a43bf 100644 --- a/modules/hal_nxp/mcux/mcux-sdk-ng/drivers/drivers.cmake +++ b/modules/hal_nxp/mcux/mcux-sdk-ng/drivers/drivers.cmake @@ -64,6 +64,8 @@ set_variable_ifdef(CONFIG_DMA_MCUX_EDMA_V3 CONFIG_MCUX_COMPONENT_driver.dma set_variable_ifdef(CONFIG_DMA_MCUX_EDMA_V4 CONFIG_MCUX_COMPONENT_driver.edma4) set_variable_ifdef(CONFIG_DMA_NXP_EDMA CONFIG_MCUX_COMPONENT_driver.edma_rev2) set_variable_ifdef(CONFIG_DMA_MCUX_EDMA_V5 CONFIG_MCUX_COMPONENT_driver.edma4) +set_variable_ifdef(CONFIG_EDAC_NXP_EIM CONFIG_MCUX_COMPONENT_driver.eim) +set_variable_ifdef(CONFIG_EDAC_NXP_ERM CONFIG_MCUX_COMPONENT_driver.erm) set_variable_ifdef(CONFIG_ENTROPY_MCUX_RNGA CONFIG_MCUX_COMPONENT_driver.rnga) set_variable_ifdef(CONFIG_ENTROPY_MCUX_TRNG CONFIG_MCUX_COMPONENT_driver.trng) set_variable_ifdef(CONFIG_ENTROPY_MCUX_CAAM CONFIG_MCUX_COMPONENT_driver.caam) diff --git a/samples/subsys/edac/README.rst b/samples/subsys/edac/README.rst index 3a1613d03d8c6..c524312df4836 100644 --- a/samples/subsys/edac/README.rst +++ b/samples/subsys/edac/README.rst @@ -63,8 +63,8 @@ Injection help can be received with: inject - Inject ECC error commands edac inject Subcommands: - addr :Get / Set physical address - mask :Get / Set address mask + param1 :Get / Set injection param 1 + param2 :Get / Set injection param 2 trigger :Trigger injection error_type :Get / Set injection error type disable_nmi :Disable NMI @@ -111,8 +111,8 @@ following devmem commands: Using data width 32 Read value 0xabcd -We should get the following message on screen indicating an IBECC event: +We should get the following message on screen indicating an ECC event: .. code-block:: none - Got notification about IBECC event + Got notification about ECC event diff --git a/samples/subsys/edac/boards/frdm_mcxa153.overlay b/samples/subsys/edac/boards/frdm_mcxa153.overlay new file mode 100644 index 0000000000000..de631052c1c3e --- /dev/null +++ b/samples/subsys/edac/boards/frdm_mcxa153.overlay @@ -0,0 +1,16 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + sram0_noecc: memory@20002000 { + compatible = "mmio-sram"; + reg = <0x20002000 DT_SIZE_K(16)>; + }; + + chosen { + zephyr,sram = &sram0_noecc; + }; +}; diff --git a/samples/subsys/edac/boards/intel_ehl_crb.conf b/samples/subsys/edac/boards/intel_ehl_crb.conf new file mode 100644 index 0000000000000..38b5b28be4835 --- /dev/null +++ b/samples/subsys/edac/boards/intel_ehl_crb.conf @@ -0,0 +1 @@ +CONFIG_EDAC_IBECC=y diff --git a/samples/subsys/edac/prj.conf b/samples/subsys/edac/prj.conf index 01695d093bd1b..89b6d6b530c2e 100644 --- a/samples/subsys/edac/prj.conf +++ b/samples/subsys/edac/prj.conf @@ -1,7 +1,6 @@ CONFIG_LOG=y CONFIG_EDAC=y -CONFIG_EDAC_IBECC=y CONFIG_EDAC_ERROR_INJECT=y CONFIG_EDAC_LOG_LEVEL_ERR=y diff --git a/samples/subsys/edac/src/main.c b/samples/subsys/edac/src/main.c index cc69c502f013b..6f57e05d7b1ca 100644 --- a/samples/subsys/edac/src/main.c +++ b/samples/subsys/edac/src/main.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2020 Intel Corporation + * Copyright 2025 NXP * * SPDX-License-Identifier: Apache-2.0 */ @@ -13,8 +14,8 @@ #include LOG_MODULE_REGISTER(main, CONFIG_LOG_DEFAULT_LEVEL); -#define STACKSIZE 1024 -#define PRIORITY 7 +#define STACKSIZE 1024 +#define PRIORITY 15 static atomic_t handled; @@ -32,7 +33,7 @@ static void notification_callback(const struct device *dev, void *data) int main(void) { - const struct device *const dev = DEVICE_DT_GET(DT_NODELABEL(ibecc)); + const struct device *const dev = DEVICE_DT_GET_OR_NULL(DT_CHOSEN(zephyr_edac)); if (!device_is_ready(dev)) { printk("%s: device not ready.\n", dev->name); @@ -54,11 +55,10 @@ void thread_function(void) while (true) { if (atomic_cas(&handled, true, false)) { - printk("Got notification about IBECC event\n"); + printk("Got notification about ECC event\n"); k_sleep(K_MSEC(300)); } } } -K_THREAD_DEFINE(thread_id, STACKSIZE, thread_function, NULL, NULL, NULL, - PRIORITY, 0, 0); +K_THREAD_DEFINE(thread_edac, STACKSIZE, thread_function, NULL, NULL, NULL, PRIORITY, 0, 0); diff --git a/scripts/checkpatch/typedefsfile b/scripts/checkpatch/typedefsfile index ad569c5e33042..cb6632bd60953 100644 --- a/scripts/checkpatch/typedefsfile +++ b/scripts/checkpatch/typedefsfile @@ -8,3 +8,4 @@ io_rw_32 Pwm FILE NRF_GPIO_Type +EIM_Type diff --git a/soc/nxp/mcx/mcxa/soc.c b/soc/nxp/mcx/mcxa/soc.c index 363a9dad44e01..694a9e955da61 100644 --- a/soc/nxp/mcx/mcxa/soc.c +++ b/soc/nxp/mcx/mcxa/soc.c @@ -1,5 +1,5 @@ /* - * Copyright 2024 NXP + * Copyright 2024-2025 NXP * * SPDX-License-Identifier: Apache-2.0 */ @@ -23,3 +23,8 @@ void soc_reset_hook(void) SystemInit(); } #endif + +void enable_ecc(uint32_t mask) +{ + SYSCON->RAM_CTRL = mask; +} diff --git a/soc/nxp/mcx/mcxa/soc.h b/soc/nxp/mcx/mcxa/soc.h index 8f2dda6f95e16..3ba14ce32eb43 100644 --- a/soc/nxp/mcx/mcxa/soc.h +++ b/soc/nxp/mcx/mcxa/soc.h @@ -19,6 +19,8 @@ extern "C" { #endif +void enable_ecc(uint32_t mask); + #ifdef __cplusplus } #endif diff --git a/soc/nxp/mcx/mcxn/soc.c b/soc/nxp/mcx/mcxn/soc.c index 91156e1c2f291..42334f00bf756 100644 --- a/soc/nxp/mcx/mcxn/soc.c +++ b/soc/nxp/mcx/mcxn/soc.c @@ -1,5 +1,5 @@ /* - * Copyright 2024 NXP + * Copyright 2024-2025 NXP * * SPDX-License-Identifier: Apache-2.0 */ @@ -68,3 +68,8 @@ static int second_core_boot(void) SYS_INIT(second_core_boot, PRE_KERNEL_2, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); #endif + +void enable_ecc(uint32_t mask) +{ + SYSCON->ECC_ENABLE_CTRL = mask; +} diff --git a/soc/nxp/mcx/mcxn/soc.h b/soc/nxp/mcx/mcxn/soc.h index ac0dc3d9f634d..68eb3e34afbe8 100644 --- a/soc/nxp/mcx/mcxn/soc.h +++ b/soc/nxp/mcx/mcxn/soc.h @@ -21,6 +21,7 @@ extern "C" { int flexspi_clock_set_freq(uint32_t clock_name, uint32_t rate); void flexspi_clock_safe_config(void); +void enable_ecc(uint32_t mask); #ifdef __cplusplus } diff --git a/west.yml b/west.yml index d2a7b04feda98..6e2c3467c07e6 100644 --- a/west.yml +++ b/west.yml @@ -210,7 +210,7 @@ manifest: groups: - hal - name: hal_nxp - revision: a7f64ac242138179b7f893eb440ccb0c5655f8e9 + revision: c4824f704c4fded29e7a37db5019e2744560bf26 path: modules/hal/nxp groups: - hal