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/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/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/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/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 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..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/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/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/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 }