From: santino.wang <santino.wang@mediatek.com>
To: Matthias Brugger <matthias.bgg@gmail.com>,
AngeloGioacchino Del Regno
<angelogioacchino.delregno@collabora.com>
Cc: <linux-mediatek@lists.infradead.org>,
<linux-arm-kernel@lists.infradead.org>,
<linux-kernel@vger.kernel.org>, <light.chen@mediatek.com>,
<Fred-WY.Chen@mediatek.com>, <arnab.layek@mediatek.com>,
Santino Wang <santino.wang@mediatek.com>
Subject: [RFC PATCH 07/11] soc: mediatek: dram-ecc: add containment policy and storm protection
Date: Mon, 2 Nov 2026 17:23:29 +0800 [thread overview]
Message-ID: <20261102092334.138759-8-santino.wang@mediatek.com> (raw)
In-Reply-To: <20261102092334.138759-1-santino.wang@mediatek.com>
From: Santino Wang <santino.wang@mediatek.com>
Add the sysfs attributes that configure the error handling done by the
secure firmware and push each setting to it through SMC:
- whether uncorrectable errors are contained and the default severity
of a contained error,
- the CE storm protection threshold, time window and cool-down,
- per address range severity overrides.
Signed-off-by: Santino Wang <santino.wang@mediatek.com>
---
drivers/soc/mediatek/dram-ecc/core/Makefile | 6 +-
drivers/soc/mediatek/dram-ecc/core/main.c | 9 +-
.../soc/mediatek/dram-ecc/core/mtk_dram_ecc.h | 3 +
.../dram-ecc/core/policy/address_range.c | 120 +++++++++++++++
.../dram-ecc/core/policy/containment.c | 107 ++++++++++++++
.../mediatek/dram-ecc/core/policy/policy.c | 50 +++++++
.../mediatek/dram-ecc/core/policy/policy.h | 19 +++
.../dram-ecc/core/policy/storm_protection.c | 137 ++++++++++++++++++
8 files changed, 449 insertions(+), 2 deletions(-)
create mode 100644 drivers/soc/mediatek/dram-ecc/core/policy/address_range.c
create mode 100644 drivers/soc/mediatek/dram-ecc/core/policy/containment.c
create mode 100644 drivers/soc/mediatek/dram-ecc/core/policy/policy.c
create mode 100644 drivers/soc/mediatek/dram-ecc/core/policy/policy.h
create mode 100644 drivers/soc/mediatek/dram-ecc/core/policy/storm_protection.c
diff --git a/drivers/soc/mediatek/dram-ecc/core/Makefile b/drivers/soc/mediatek/dram-ecc/core/Makefile
index 96d37c6a3b9c..d8d878f321d4 100644
--- a/drivers/soc/mediatek/dram-ecc/core/Makefile
+++ b/drivers/soc/mediatek/dram-ecc/core/Makefile
@@ -8,6 +8,10 @@ mtk_dram_ecc-y := \
ghes_notifier.o \
trace.o \
counters.o \
- addresses.o
+ addresses.o \
+ policy/policy.o \
+ policy/containment.o \
+ policy/storm_protection.o \
+ policy/address_range.o
CFLAGS_trace.o := -I$(src)
diff --git a/drivers/soc/mediatek/dram-ecc/core/main.c b/drivers/soc/mediatek/dram-ecc/core/main.c
index b859af25bcc0..315bdcbd650a 100644
--- a/drivers/soc/mediatek/dram-ecc/core/main.c
+++ b/drivers/soc/mediatek/dram-ecc/core/main.c
@@ -37,10 +37,14 @@ static int __init mtk_dram_ecc_init(void)
goto err_class;
}
- ret = mtk_dram_ecc_counters_init(&mtk_dram_ecc_dev->kobj);
+ ret = mtk_dram_ecc_policy_init(mtk_dram_ecc_dev);
if (ret)
goto err_dev;
+ ret = mtk_dram_ecc_counters_init(&mtk_dram_ecc_dev->kobj);
+ if (ret)
+ goto err_policy;
+
ret = mtk_dram_ecc_addresses_init(&mtk_dram_ecc_dev->kobj);
if (ret)
goto err_counters;
@@ -62,6 +66,8 @@ static int __init mtk_dram_ecc_init(void)
mtk_dram_ecc_addresses_exit();
err_counters:
mtk_dram_ecc_counters_exit();
+err_policy:
+ mtk_dram_ecc_policy_exit(mtk_dram_ecc_dev);
err_dev:
device_destroy(mtk_dram_ecc_class, MKDEV(0, 0));
err_class:
@@ -79,6 +85,7 @@ static void __exit mtk_dram_ecc_exit(void)
mtk_dram_ecc_ghes_exit();
mtk_dram_ecc_addresses_exit();
mtk_dram_ecc_counters_exit();
+ mtk_dram_ecc_policy_exit(mtk_dram_ecc_dev);
device_destroy(mtk_dram_ecc_class, MKDEV(0, 0));
class_destroy(mtk_dram_ecc_class);
trace_mtk_dram_ecc_lifecycle(1);
diff --git a/drivers/soc/mediatek/dram-ecc/core/mtk_dram_ecc.h b/drivers/soc/mediatek/dram-ecc/core/mtk_dram_ecc.h
index 456961818ccd..c8a75450dac5 100644
--- a/drivers/soc/mediatek/dram-ecc/core/mtk_dram_ecc.h
+++ b/drivers/soc/mediatek/dram-ecc/core/mtk_dram_ecc.h
@@ -21,4 +21,7 @@ struct mtk_dram_ecc_data {
extern struct mtk_dram_ecc_data mtk_ecc_data;
+int mtk_dram_ecc_policy_init(struct device *dev);
+void mtk_dram_ecc_policy_exit(struct device *dev);
+
#endif /* _MTK_DRAM_ECC_H_ */
diff --git a/drivers/soc/mediatek/dram-ecc/core/policy/address_range.c b/drivers/soc/mediatek/dram-ecc/core/policy/address_range.c
new file mode 100644
index 000000000000..4ead23d52d7b
--- /dev/null
+++ b/drivers/soc/mediatek/dram-ecc/core/policy/address_range.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026 MediaTek Inc.
+ */
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include <linux/string.h>
+
+#include "../mtk_dram_ecc.h"
+#include "../smc.h"
+#include "policy.h"
+
+/*
+ * Kernel-side sanity cap on entries_show()'s enumeration loop only - not
+ * the real limit. ATF owns the actual table size (policy.c's
+ * DRAM_ECC_MAX_RANGE_ENTRIES) and is the source of truth; this just
+ * bounds the loop in case ATF ever misbehaves.
+ */
+#define ADDRESS_RANGE_ENUM_CAP 64
+
+static struct kobject *address_range_kobj;
+
+static ssize_t entries_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ ssize_t len = 0;
+ u32 i;
+
+ for (i = 0; i < ADDRESS_RANGE_ENUM_CAP; i++) {
+ u64 addr_from, addr_to;
+ bool recoverable;
+
+ if (mtk_dram_ecc_smc_range_get(i, &addr_from, &addr_to, &recoverable))
+ break;
+
+ len += sysfs_emit_at(buf, len, "0x%llx 0x%llx %s\n",
+ addr_from, addr_to, recoverable ? "recoverable" : "fatal");
+ }
+
+ return len;
+}
+
+/*
+ * entries_store - add or update one range. Format: "addr_from addr_to
+ * fatal|recoverable". No stored state on the kernel side - ATF holds the
+ * table; entries_show() always reads it back fresh.
+ */
+static ssize_t entries_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ u64 addr_from, addr_to;
+ char severity_str[16];
+ bool recoverable;
+ int ret;
+
+ if (sscanf(buf, "%llx %llx %15s", &addr_from, &addr_to, severity_str) != 3)
+ return -EINVAL;
+
+ if (sysfs_streq(severity_str, "recoverable"))
+ recoverable = true;
+ else if (sysfs_streq(severity_str, "fatal"))
+ recoverable = false;
+ else
+ return -EINVAL;
+
+ ret = mtk_dram_ecc_smc_range_add(addr_from, addr_to, recoverable);
+ if (ret)
+ return -EIO;
+
+ return count;
+}
+
+/* clear_store - any write clears the whole ATF-side range table. */
+static ssize_t clear_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret = mtk_dram_ecc_smc_range_clear();
+
+ if (ret)
+ return -EIO;
+
+ return count;
+}
+
+static struct kobj_attribute entries_attr =
+ __ATTR(entries, 0644, entries_show, entries_store);
+static struct kobj_attribute clear_attr =
+ __ATTR(clear, 0200, NULL, clear_store);
+
+static struct attribute *attrs[] = {
+ &entries_attr.attr,
+ &clear_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group group = {
+ .attrs = attrs,
+};
+
+int address_range_init(struct kobject *parent)
+{
+ int ret;
+
+ address_range_kobj = kobject_create_and_add("address_range", parent);
+ if (!address_range_kobj)
+ return -ENOMEM;
+
+ ret = sysfs_create_group(address_range_kobj, &group);
+ if (ret) {
+ kobject_put(address_range_kobj);
+ return ret;
+ }
+ return 0;
+}
+
+void address_range_exit(struct kobject *parent)
+{
+ sysfs_remove_group(address_range_kobj, &group);
+ kobject_put(address_range_kobj);
+}
diff --git a/drivers/soc/mediatek/dram-ecc/core/policy/containment.c b/drivers/soc/mediatek/dram-ecc/core/policy/containment.c
new file mode 100644
index 000000000000..98c7ec6fafb0
--- /dev/null
+++ b/drivers/soc/mediatek/dram-ecc/core/policy/containment.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026 MediaTek Inc.
+ */
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include <linux/string.h>
+
+#include "../mtk_dram_ecc.h"
+#include "../smc.h"
+#include "policy.h"
+
+static struct kobject *containment_kobj;
+
+static ssize_t enabled_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ bool enabled;
+
+ if (mtk_dram_ecc_smc_containment_get_enabled(&enabled))
+ return -EIO;
+ return sysfs_emit(buf, "%d\n", enabled ? 1 : 0);
+}
+
+static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ bool val;
+ int ret;
+
+ ret = kstrtobool(buf, &val);
+ if (ret)
+ return ret;
+
+ ret = mtk_dram_ecc_smc_containment_set_enabled(val);
+ if (ret)
+ return -EIO;
+
+ return count;
+}
+
+static ssize_t default_severity_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ bool recoverable;
+
+ if (mtk_dram_ecc_smc_containment_get_default_severity(&recoverable))
+ return -EIO;
+ return sysfs_emit(buf, "%s\n", recoverable ? "recoverable" : "fatal");
+}
+
+static ssize_t default_severity_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ bool recoverable;
+ int ret;
+
+ if (sysfs_streq(buf, "recoverable"))
+ recoverable = true;
+ else if (sysfs_streq(buf, "fatal"))
+ recoverable = false;
+ else
+ return -EINVAL;
+
+ ret = mtk_dram_ecc_smc_containment_set_default_severity(recoverable);
+ if (ret)
+ return -EIO;
+
+ return count;
+}
+
+static struct kobj_attribute enabled_attr =
+ __ATTR(enabled, 0644, enabled_show, enabled_store);
+static struct kobj_attribute default_severity_attr =
+ __ATTR(default_severity, 0644, default_severity_show, default_severity_store);
+
+static struct attribute *attrs[] = {
+ &enabled_attr.attr,
+ &default_severity_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group group = {
+ .attrs = attrs,
+};
+
+int containment_init(struct kobject *parent)
+{
+ int ret;
+
+ containment_kobj = kobject_create_and_add("containment", parent);
+ if (!containment_kobj)
+ return -ENOMEM;
+
+ ret = sysfs_create_group(containment_kobj, &group);
+ if (ret) {
+ kobject_put(containment_kobj);
+ return ret;
+ }
+ return 0;
+}
+
+void containment_exit(struct kobject *parent)
+{
+ sysfs_remove_group(containment_kobj, &group);
+ kobject_put(containment_kobj);
+}
diff --git a/drivers/soc/mediatek/dram-ecc/core/policy/policy.c b/drivers/soc/mediatek/dram-ecc/core/policy/policy.c
new file mode 100644
index 000000000000..ecd0a430de6d
--- /dev/null
+++ b/drivers/soc/mediatek/dram-ecc/core/policy/policy.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026 MediaTek Inc.
+ */
+#include <linux/kobject.h>
+#include <linux/device.h>
+
+#include "../mtk_dram_ecc.h"
+#include "policy.h"
+
+static struct kobject *policy_kobj;
+
+int mtk_dram_ecc_policy_init(struct device *dev)
+{
+ int ret;
+
+ policy_kobj = kobject_create_and_add("policy", &dev->kobj);
+ if (!policy_kobj)
+ return -ENOMEM;
+
+ ret = containment_init(policy_kobj);
+ if (ret)
+ goto err_policy;
+
+ ret = storm_protection_init(policy_kobj);
+ if (ret)
+ goto err_containment;
+
+ ret = address_range_init(policy_kobj);
+ if (ret)
+ goto err_storm;
+
+ return 0;
+
+err_storm:
+ storm_protection_exit(policy_kobj);
+err_containment:
+ containment_exit(policy_kobj);
+err_policy:
+ kobject_put(policy_kobj);
+ return ret;
+}
+
+void mtk_dram_ecc_policy_exit(struct device *dev)
+{
+ address_range_exit(policy_kobj);
+ storm_protection_exit(policy_kobj);
+ containment_exit(policy_kobj);
+ kobject_put(policy_kobj);
+}
diff --git a/drivers/soc/mediatek/dram-ecc/core/policy/policy.h b/drivers/soc/mediatek/dram-ecc/core/policy/policy.h
new file mode 100644
index 000000000000..a535e2ce0e6d
--- /dev/null
+++ b/drivers/soc/mediatek/dram-ecc/core/policy/policy.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2026 MediaTek Inc.
+ */
+#ifndef _MTK_DRAM_ECC_POLICY_H_
+#define _MTK_DRAM_ECC_POLICY_H_
+
+#include <linux/kobject.h>
+
+int containment_init(struct kobject *parent);
+void containment_exit(struct kobject *parent);
+
+int storm_protection_init(struct kobject *parent);
+void storm_protection_exit(struct kobject *parent);
+
+int address_range_init(struct kobject *parent);
+void address_range_exit(struct kobject *parent);
+
+#endif /* _MTK_DRAM_ECC_POLICY_H_ */
diff --git a/drivers/soc/mediatek/dram-ecc/core/policy/storm_protection.c b/drivers/soc/mediatek/dram-ecc/core/policy/storm_protection.c
new file mode 100644
index 000000000000..971ad8bdbaae
--- /dev/null
+++ b/drivers/soc/mediatek/dram-ecc/core/policy/storm_protection.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026 MediaTek Inc.
+ */
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include <linux/string.h>
+
+#include "../mtk_dram_ecc.h"
+#include "../smc.h"
+#include "policy.h"
+
+static struct kobject *storm_protection_kobj;
+
+static ssize_t threshold_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ u32 val;
+
+ if (mtk_dram_ecc_smc_storm_get_threshold(&val))
+ return -EIO;
+ return sysfs_emit(buf, "%u\n", val);
+}
+
+static ssize_t threshold_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ u32 val;
+ int ret;
+
+ ret = kstrtou32(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ ret = mtk_dram_ecc_smc_storm_set_threshold(val);
+ if (ret)
+ return -EIO;
+
+ return count;
+}
+
+static ssize_t time_window_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ u32 val;
+
+ if (mtk_dram_ecc_smc_storm_get_time_window(&val))
+ return -EIO;
+ return sysfs_emit(buf, "%u\n", val);
+}
+
+static ssize_t time_window_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ u32 val;
+ int ret;
+
+ ret = kstrtou32(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ ret = mtk_dram_ecc_smc_storm_set_time_window(val);
+ if (ret)
+ return -EIO;
+
+ return count;
+}
+
+static ssize_t cooldown_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ u32 val;
+
+ if (mtk_dram_ecc_smc_storm_get_cooldown(&val))
+ return -EIO;
+ return sysfs_emit(buf, "%u\n", val);
+}
+
+static ssize_t cooldown_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ u32 val;
+ int ret;
+
+ ret = kstrtou32(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ ret = mtk_dram_ecc_smc_storm_set_cooldown(val);
+ if (ret)
+ return -EIO;
+
+ return count;
+}
+
+static struct kobj_attribute threshold_attr =
+ __ATTR_RW(threshold);
+static struct kobj_attribute time_window_attr =
+ __ATTR_RW(time_window);
+static struct kobj_attribute cooldown_attr =
+ __ATTR_RW(cooldown);
+
+static struct attribute *attrs[] = {
+ &threshold_attr.attr,
+ &time_window_attr.attr,
+ &cooldown_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group group = {
+ .attrs = attrs,
+};
+
+int storm_protection_init(struct kobject *parent)
+{
+ int ret;
+
+ storm_protection_kobj = kobject_create_and_add("storm_protection", parent);
+ if (!storm_protection_kobj)
+ return -ENOMEM;
+
+ ret = sysfs_create_group(storm_protection_kobj, &group);
+ if (ret) {
+ kobject_put(storm_protection_kobj);
+ return ret;
+ }
+ return 0;
+}
+
+void storm_protection_exit(struct kobject *parent)
+{
+ sysfs_remove_group(storm_protection_kobj, &group);
+ kobject_put(storm_protection_kobj);
+}
--
2.45.2
next prev parent reply other threads:[~2026-11-02 9:25 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-02 9:23 [RFC PATCH 00/11] soc: mediatek: add a DRAM ECC error handling driver santino.wang
2026-11-02 9:23 ` [RFC PATCH 01/11] soc: mediatek: dram-ecc: add the DRAM ECC driver skeleton santino.wang
2026-11-02 13:41 ` Krzysztof Kozlowski
2026-11-02 9:23 ` [RFC PATCH 02/11] soc: mediatek: dram-ecc: add the SMC interface to the secure firmware santino.wang
2026-11-02 9:23 ` [RFC PATCH 03/11] soc: mediatek: dram-ecc: add tracepoints santino.wang
2026-11-02 9:23 ` [RFC PATCH 04/11] soc: mediatek: dram-ecc: handle GHES vendor records from the firmware santino.wang
2026-11-02 9:23 ` [RFC PATCH 05/11] soc: mediatek: dram-ecc: add error counters and the address lists santino.wang
2026-11-02 13:42 ` Krzysztof Kozlowski
2026-11-02 9:23 ` [RFC PATCH 06/11] soc: mediatek: dram-ecc: keep error counters across boots santino.wang
2026-11-02 9:23 ` santino.wang [this message]
2026-11-02 9:23 ` [RFC PATCH 08/11] soc: mediatek: dram-ecc: add the corrected error leaky bucket santino.wang
2026-11-02 9:23 ` [RFC PATCH 09/11] soc: mediatek: dram-ecc: add a generic netlink event interface santino.wang
2026-11-02 9:23 ` [RFC PATCH 10/11] soc: mediatek: dram-ecc: report an uncorrectable error of the previous boot santino.wang
2026-11-02 13:44 ` Krzysztof Kozlowski
2026-11-02 9:23 ` [RFC PATCH 11/11] Documentation: ABI: add the sysfs attributes of the MediaTek DRAM ECC driver santino.wang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261102092334.138759-8-santino.wang@mediatek.com \
--to=santino.wang@mediatek.com \
--cc=Fred-WY.Chen@mediatek.com \
--cc=angelogioacchino.delregno@collabora.com \
--cc=arnab.layek@mediatek.com \
--cc=light.chen@mediatek.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mediatek@lists.infradead.org \
--cc=matthias.bgg@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®