From 44ba5d4307eb6fbe5ae41ea751a9acf72e96e228 Mon Sep 17 00:00:00 2001 From: Yves Wang Date: Tue, 5 Aug 2025 14:47:45 +0800 Subject: [PATCH 1/3] drivers: edac: Add NXP EDAC driver Add edac driver for NXP's ERM and EIM peripherals. It can inject ECC error to specific channel within EIM and then report the error address, syndrome and count within ERM. Make the edac shell sample more generic for new platforms. Signed-off-by: Yves Wang --- drivers/edac/CMakeLists.txt | 2 + drivers/edac/Kconfig | 2 + drivers/edac/Kconfig.mcux_erm | 27 ++ drivers/edac/edac_mcux_erm.c | 331 ++++++++++++++++++ dts/bindings/edac/nxp,eim.yaml | 30 ++ dts/bindings/edac/nxp,erm.yaml | 15 + include/zephyr/drivers/edac/edac_mcux_erm.h | 21 ++ .../mcux/mcux-sdk-ng/drivers/drivers.cmake | 2 + samples/subsys/edac/boards/intel_ehl_crb.conf | 1 + samples/subsys/edac/prj.conf | 2 +- samples/subsys/edac/src/main.c | 12 +- 11 files changed, 438 insertions(+), 7 deletions(-) create mode 100644 drivers/edac/Kconfig.mcux_erm create mode 100644 drivers/edac/edac_mcux_erm.c create mode 100644 dts/bindings/edac/nxp,eim.yaml create mode 100644 dts/bindings/edac/nxp,erm.yaml create mode 100644 include/zephyr/drivers/edac/edac_mcux_erm.h create mode 100644 samples/subsys/edac/boards/intel_ehl_crb.conf diff --git a/drivers/edac/CMakeLists.txt b/drivers/edac/CMakeLists.txt index 277bcb937ec51..9c8c0224ed8eb 100644 --- a/drivers/edac/CMakeLists.txt +++ b/drivers/edac/CMakeLists.txt @@ -1,8 +1,10 @@ # Copyright (c) 2020 Intel Corporation +# Copyright 2025 NXP # SPDX-License-Identifier: Apache-2.0 zephyr_library() zephyr_library_sources_ifdef(CONFIG_EDAC_IBECC edac_ibecc.c) zephyr_library_sources_ifdef(CONFIG_EDAC_SYNOPSYS edac_synopsys.c) +zephyr_library_sources_ifdef(CONFIG_EDAC_NXP_ERM edac_mcux_erm.c) zephyr_library_sources_ifdef(CONFIG_EDAC_SHELL shell.c) 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..731148608e4df --- /dev/null +++ b/drivers/edac/Kconfig.mcux_erm @@ -0,0 +1,27 @@ +# 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_ERROR_INJECT + depends on EDAC_NXP_EIM diff --git a/drivers/edac/edac_mcux_erm.c b/drivers/edac/edac_mcux_erm.c new file mode 100644 index 0000000000000..d46e29228abfa --- /dev/null +++ b/drivers/edac/edac_mcux_erm.c @@ -0,0 +1,331 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(edac_mcux_erm, CONFIG_EDAC_LOG_LEVEL); + +#if defined(CONFIG_EDAC_ERROR_INJECT) && CONFIG_EDAC_ERROR_INJECT +#include + +#define EDAC_NXP_SINGLE_BIT_ERROR_MASK 0x1 +#define EDAC_NXP_DOUBLE_BIT_ERROR_MASK 0x3 + +struct edac_nxp_eim_channel { + uint32_t start_address; + uint32_t size; + uint32_t inject_enable; + uint32_t ecc_enable; + uint8_t channel_id; +}; +#endif /* CONFIG_EDAC_ERROR_INJECT */ + +struct edac_nxp_config { + ERM_Type *erm_base; +#if defined(CONFIG_EDAC_ERROR_INJECT) && CONFIG_EDAC_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; +#if defined(CONFIG_EDAC_ERROR_INJECT) && CONFIG_EDAC_ERROR_INJECT + uint32_t eim_channel; + uint32_t offset; + 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; +} + +#if defined(CONFIG_EDAC_ERROR_INJECT) && CONFIG_EDAC_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_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 (data->eim_channel != data->erm_channel) { + LOG_WRN("EIM inject error on channel %x but ERM was listening on channel %x set " + "ERM channel be same with EIM channel", + data->eim_channel, data->erm_channel); + data->erm_channel = data->eim_channel; + } + enable_ecc(eim_channel_data->ecc_enable); + EIM_InjectDataBitError(config->eim_base, data->eim_channel, inject_data); + EIM_EnableErrorInjectionChannels(config->eim_base, eim_channel_data->inject_enable); + 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 ecc_error_log_get(const struct device *dev, uint64_t *value) +{ + const struct edac_nxp_config *config = dev->config; + struct edac_nxp_data *data = dev->data; + + *value = ERM_GetSyndrome(config->erm_base, data->erm_channel); + return 0; +} + +static int ecc_error_log_clear(const struct device *dev) +{ + const struct edac_nxp_config *config = dev->config; + struct edac_nxp_data *data = dev->data; + + ERM_ClearInterruptStatus(config->erm_base, data->erm_channel, kERM_AllInterruptsEnable); + return 0; +} + +static int errors_cor_get(const struct device *dev) +{ + 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); +} + +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); + struct edac_nxp_callback_data cb_data = { + .corr_err_count = ERM_GetErrorCount(config->erm_base, data->erm_channel), + .err_syndrome = ERM_GetSyndrome(config->erm_base, data->erm_channel), + .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 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 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) = { +#if defined(CONFIG_EDAC_ERROR_INJECT) && CONFIG_EDAC_ERROR_INJECT + /* Error Injection functions */ + .inject_set_param1 = inject_set_param1, + .inject_get_param1 = inject_get_param1, + .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 */ + + /* Error reporting & clearing functions */ + .ecc_error_log_get = ecc_error_log_get, + .ecc_error_log_clear = ecc_error_log_clear, + + /* 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; + +#if defined(CONFIG_EDAC_ERROR_INJECT) && CONFIG_EDAC_ERROR_INJECT + EIM_Init(config->eim_base); + EIM_EnableGlobalErrorInjection(config->eim_base, true); + 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_AllInterruptsEnable); + 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 + +#if defined(CONFIG_EDAC_ERROR_INJECT) && CONFIG_EDAC_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), \ + .start_address = DT_PROP(node, start_address), \ + .inject_enable = DT_PROP(node, inject_enable), \ + .ecc_enable = DT_PROP(node, ecc_enable), \ + .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/bindings/edac/nxp,eim.yaml b/dts/bindings/edac/nxp,eim.yaml new file mode 100644 index 0000000000000..87fc3ec5c44a8 --- /dev/null +++ b/dts/bindings/edac/nxp,eim.yaml @@ -0,0 +1,30 @@ +# 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 + + size: + required: true + type: int + + start-address: + required: true + type: int + + inject-enable: + required: true + type: int + + ecc-enable: + required: true + type: int diff --git a/dts/bindings/edac/nxp,erm.yaml b/dts/bindings/edac/nxp,erm.yaml new file mode 100644 index 0000000000000..f20f1deabbd44 --- /dev/null +++ b/dts/bindings/edac/nxp,erm.yaml @@ -0,0 +1,15 @@ +# 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..045643fb6113c --- /dev/null +++ b/include/zephyr/drivers/edac/edac_mcux_erm.h @@ -0,0 +1,21 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_EDAC_NXP_H_ +#define ZEPHYR_DRIVERS_EDAC_NXP_H_ +/* Callback data provided to function passed to notify_cb_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_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 e1ecc7c2bcb4a..6a9002d98462d 100644 --- a/modules/hal_nxp/mcux/mcux-sdk-ng/drivers/drivers.cmake +++ b/modules/hal_nxp/mcux/mcux-sdk-ng/drivers/drivers.cmake @@ -63,6 +63,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/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..5f3c2e3c72f61 100644 --- a/samples/subsys/edac/prj.conf +++ b/samples/subsys/edac/prj.conf @@ -1,7 +1,7 @@ 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); From e411a03da72a826645e89c3240e88e15556e1074 Mon Sep 17 00:00:00 2001 From: Yves Wang Date: Tue, 5 Aug 2025 15:22:56 +0800 Subject: [PATCH 2/3] dts: add edac support for frdm_mcxa153 and frdm_mcxn236/947 Add eim, erm and edac instance for 3 nxp mcx boards Signed-off-by: Yves Wang --- boards/nxp/frdm_mcxa153/frdm_mcxa153.dts | 13 ++++ boards/nxp/frdm_mcxn236/frdm_mcxn236.dts | 25 ++++++ boards/nxp/frdm_mcxn947/frdm_mcxn947.dtsi | 36 +++++++++ dts/arm/nxp/nxp_mcxa153.dtsi | 26 +++++++ dts/arm/nxp/nxp_mcxn23x_common.dtsi | 60 ++++++++++++++ dts/arm/nxp/nxp_mcxnx4x_common.dtsi | 78 +++++++++++++++++++ .../subsys/edac/boards/frdm_mcxa153.overlay | 16 ++++ samples/subsys/edac/sample.yaml | 6 +- soc/nxp/mcx/mcxa/soc.c | 7 +- soc/nxp/mcx/mcxa/soc.h | 2 + soc/nxp/mcx/mcxn/soc.c | 7 +- soc/nxp/mcx/mcxn/soc.h | 1 + 12 files changed, 274 insertions(+), 3 deletions(-) create mode 100644 samples/subsys/edac/boards/frdm_mcxa153.overlay diff --git a/boards/nxp/frdm_mcxa153/frdm_mcxa153.dts b/boards/nxp/frdm_mcxa153/frdm_mcxa153.dts index e257a5e0bd986..61ce44435f98d 100644 --- a/boards/nxp/frdm_mcxa153/frdm_mcxa153.dts +++ b/boards/nxp/frdm_mcxa153/frdm_mcxa153.dts @@ -34,6 +34,7 @@ zephyr,code-partition = &slot0_partition; zephyr,console = &lpuart0; zephyr,shell-uart = &lpuart0; + zephyr,edac = &erm0; }; leds { @@ -80,6 +81,18 @@ status = "okay"; }; +&eim0 { + status = "okay"; +}; + +&eim0_channel0 { + status = "okay"; +}; + +&erm0 { + status = "okay"; +}; + &edma0 { status = "okay"; }; diff --git a/boards/nxp/frdm_mcxn236/frdm_mcxn236.dts b/boards/nxp/frdm_mcxn236/frdm_mcxn236.dts index 6375c643dc2e8..45b959c791f72 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 6fd415f581c75..19b8df5780feb 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"; @@ -111,6 +115,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/dts/arm/nxp/nxp_mcxa153.dtsi b/dts/arm/nxp/nxp_mcxa153.dtsi index e6e662aa39229..8cc173f45116a 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: RAMA0: channel0 { + channel-id = <0>; + size = ; + start-address = <0x20000000>; + inject-enable = <0x80000000>; + 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_mcxn23x_common.dtsi b/dts/arm/nxp/nxp_mcxn23x_common.dtsi index 75a7160e2226c..53f49a5e98645 100644 --- a/dts/arm/nxp/nxp_mcxn23x_common.dtsi +++ b/dts/arm/nxp/nxp_mcxn23x_common.dtsi @@ -171,6 +171,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: RAMX: channel0 { + channel-id = <0>; + size = ; + start-address = <0x4000000>; + inject-enable = <0x80000000>; + ecc-enable = <0x2>; + status = "disabled"; + }; + + eim0_channel1: RAMA: channel1 { + channel-id = <1>; + size = ; + start-address = <0x20000000>; + inject-enable = <0x40000000>; + ecc-enable = <0x1>; + status = "disabled"; + }; + + eim0_channel2: RAMB: channel2 { + channel-id = <2>; + size = ; + start-address = <0x20008000>; + inject-enable = <0x20000000>; + ecc-enable = <0x2>; + status = "disabled"; + }; + + eim0_channel3: RAMC: channel3 { + channel-id = <3>; + size = ; + start-address = <0x20010000>; + inject-enable = <0x10000000>; + ecc-enable = <0x4>; + status = "disabled"; + }; + + eim0_channel4: RAMD: channel4 { + channel-id = <4>; + size = ; + start-address = <0x20020000>; + inject-enable = <0x8000000>; + 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 68c1206e2588f..19e31d2256da9 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: RAMX: channel0 { + channel-id = <0>; + size = ; + start-address = <0x4000000>; + inject-enable = <0x80000000>; + ecc-enable = <0x2>; + status = "disabled"; + }; + + eim0_channel1: RAMA: channel1 { + channel-id = <1>; + size = ; + start-address = <0x20000000>; + inject-enable = <0x40000000>; + ecc-enable = <0x1>; + status = "disabled"; + }; + + eim0_channel2: RAMB: channel2 { + channel-id = <2>; + size = ; + start-address = <0x20008000>; + inject-enable = <0x20000000>; + ecc-enable = <0x2>; + status = "disabled"; + }; + + eim0_channel3: RAMC: channel3 { + channel-id = <3>; + size = ; + start-address = <0x20010000>; + inject-enable = <0x10000000>; + ecc-enable = <0x4>; + status = "disabled"; + }; + + eim0_channel4: RAMD: channel4 { + channel-id = <4>; + size = ; + start-address = <0x20020000>; + inject-enable = <0x8000000>; + ecc-enable = <0x4>; + status = "disabled"; + }; + + eim0_channel5: RAME: channel5 { + channel-id = <5>; + size = ; + start-address = <0x20030000>; + inject-enable = <0x4000000>; + ecc-enable = <0x8>; + status = "disabled"; + }; + + eim0_channel6: RAMF: channel6 { + channel-id = <6>; + size = ; + start-address = <0x20040000>; + inject-enable = <0x2000000>; + 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/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/sample.yaml b/samples/subsys/edac/sample.yaml index 5ad091e42cf79..e6be10ae6beaa 100644 --- a/samples/subsys/edac/sample.yaml +++ b/samples/subsys/edac/sample.yaml @@ -11,7 +11,11 @@ common: tests: sample.subsys.edac: - platform_allow: intel_ehl_crb + platform_allow: + - intel_ehl_crb + - frdm_mcxa153 + - frdm_mcxn236 + - frdm_mcxn947 integration_platforms: - intel_ehl_crb tags: edac 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 } From 84d39cdecaac14f97154e0d627687dc321848931 Mon Sep 17 00:00:00 2001 From: Yves Wang Date: Tue, 5 Aug 2025 15:26:35 +0800 Subject: [PATCH 3/3] doc: Update edac shell sample's readme Update the console log in readme to make it aligned with the actual output Signed-off-by: Yves Wang --- samples/subsys/edac/README.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/samples/subsys/edac/README.rst b/samples/subsys/edac/README.rst index 0f15d544b6999..7211d724bf8c1 100644 --- a/samples/subsys/edac/README.rst +++ b/samples/subsys/edac/README.rst @@ -65,8 +65,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 @@ -113,8 +113,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