[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20251222094351.38792-8-tianruidong@linux.alibaba.com>
Date: Mon, 22 Dec 2025 17:43:40 +0800
From: Ruidong Tian <tianruidong@...ux.alibaba.com>
To: catalin.marinas@....com,
will@...nel.org,
lpieralisi@...nel.org,
guohanjun@...wei.com,
sudeep.holla@....com,
xueshuai@...ux.alibaba.com,
linux-kernel@...r.kernel.org,
linux-acpi@...r.kernel.org,
linux-arm-kernel@...ts.infradead.org,
rafael@...nel.org,
lenb@...nel.org,
tony.luck@...el.com,
bp@...en8.de,
yazen.ghannam@....com,
misono.tomohiro@...itsu.com
Cc: tianruidong@...ux.alibaba.com
Subject: [PATCH v4 07/17] ras: AEST: Support CE threshold of error record
The CE threshold defines the number of Correctable Errors (CE) that
must occur in a record before triggering an interrupt. Error records
support multiple threshold configurations, including 8B, 16B, and 32B.
This patch detects the supported threshold settings for error records
and sets the default threshold to 1, ensuring an interrupt is generated
for every CE occurrence.
Signed-off-by: Ruidong Tian <tianruidong@...ux.alibaba.com>
---
arch/arm64/include/asm/ras.h | 41 ++++++++++++++++++++
drivers/ras/aest/aest-core.c | 74 ++++++++++++++++++++++++++++++++++++
drivers/ras/aest/aest.h | 17 +++++++++
include/linux/acpi_aest.h | 3 ++
4 files changed, 135 insertions(+)
diff --git a/arch/arm64/include/asm/ras.h b/arch/arm64/include/asm/ras.h
index da7c441252fe..6c51d27520c0 100644
--- a/arch/arm64/include/asm/ras.h
+++ b/arch/arm64/include/asm/ras.h
@@ -4,9 +4,50 @@
#include <linux/types.h>
+/* ERR<n>FR */
+#define ERR_FR_CE GENMASK_ULL(54, 53)
+#define ERR_FR_RP BIT(15)
+#define ERR_FR_CEC GENMASK_ULL(14, 12)
+
+#define ERR_FR_RP_SINGLE_COUNTER 0
+#define ERR_FR_RP_DOUBLE_COUNTER 1
+
+#define ERR_FR_CEC_0B_COUNTER 0
+#define ERR_FR_CEC_8B_COUNTER BIT(1)
+#define ERR_FR_CEC_16B_COUNTER BIT(2)
+
+/* ERR<n>MISC0 */
+
+/* ERR<n>FR.CEC == 0b010, ERR<n>FR.RP == 0 */
+#define ERR_MISC0_8B_OF BIT(39)
+#define ERR_MISC0_8B_CEC GENMASK_ULL(38, 32)
+
+/* ERR<n>FR.CEC == 0b100, ERR<n>FR.RP == 0 */
+#define ERR_MISC0_16B_OF BIT(47)
+#define ERR_MISC0_16B_CEC GENMASK_ULL(46, 32)
+
+#define ERR_MISC0_CEC_SHIFT 31
+
+#define ERR_8B_CEC_MAX (ERR_MISC0_8B_CEC >> ERR_MISC0_CEC_SHIFT)
+#define ERR_16B_CEC_MAX (ERR_MISC0_16B_CEC >> ERR_MISC0_CEC_SHIFT)
+
+/* ERR<n>FR.CEC == 0b100, ERR<n>FR.RP == 1 */
+#define ERR_MISC0_16B_OFO BIT(63)
+#define ERR_MISC0_16B_CECO GENMASK_ULL(62, 48)
+#define ERR_MISC0_16B_OFR BIT(47)
+#define ERR_MISC0_16B_CECR GENMASK_ULL(46, 32)
+
/* ERRDEVARCH */
#define ERRDEVARCH_REV GENMASK(19, 16)
+enum ras_ce_threshold {
+ RAS_CE_THRESHOLD_0B,
+ RAS_CE_THRESHOLD_8B,
+ RAS_CE_THRESHOLD_16B,
+ RAS_CE_THRESHOLD_32B,
+ UNKNOWN,
+};
+
struct ras_ext_regs {
u64 err_fr;
u64 err_ctlr;
diff --git a/drivers/ras/aest/aest-core.c b/drivers/ras/aest/aest-core.c
index 1218ae51079c..5cfe91a6d72a 100644
--- a/drivers/ras/aest/aest-core.c
+++ b/drivers/ras/aest/aest-core.c
@@ -16,6 +16,79 @@ DEFINE_PER_CPU(struct aest_device, percpu_adev);
#undef pr_fmt
#define pr_fmt(fmt) "AEST: " fmt
+static enum ras_ce_threshold aest_get_ce_threshold(struct aest_record *record)
+{
+ u64 err_fr, err_fr_cec, err_fr_rp = -1;
+
+ err_fr = record_read(record, ERXFR);
+ err_fr_cec = FIELD_GET(ERR_FR_CEC, err_fr);
+ err_fr_rp = FIELD_GET(ERR_FR_RP, err_fr);
+
+ if (err_fr_cec == ERR_FR_CEC_0B_COUNTER)
+ return RAS_CE_THRESHOLD_0B;
+ else if (err_fr_rp == ERR_FR_RP_DOUBLE_COUNTER)
+ return RAS_CE_THRESHOLD_32B;
+ else if (err_fr_cec == ERR_FR_CEC_8B_COUNTER)
+ return RAS_CE_THRESHOLD_8B;
+ else if (err_fr_cec == ERR_FR_CEC_16B_COUNTER)
+ return RAS_CE_THRESHOLD_16B;
+ else
+ return UNKNOWN;
+}
+
+static const struct ce_threshold_info ce_info[] = {
+ [RAS_CE_THRESHOLD_0B] = { 0 },
+ [RAS_CE_THRESHOLD_8B] = {
+ .max_count = ERR_8B_CEC_MAX,
+ .mask = ERR_MISC0_8B_CEC,
+ .shift = ERR_MISC0_CEC_SHIFT,
+ },
+ [RAS_CE_THRESHOLD_16B] = {
+ .max_count = ERR_16B_CEC_MAX,
+ .mask = ERR_MISC0_16B_CEC,
+ .shift = ERR_MISC0_CEC_SHIFT,
+ },
+};
+
+static void aest_set_ce_threshold(struct aest_record *record)
+{
+ u64 err_misc0;
+ struct ce_threshold *ce = &record->ce;
+ const struct ce_threshold_info *info;
+
+ record->threshold_type = aest_get_ce_threshold(record);
+
+ switch (record->threshold_type) {
+ case RAS_CE_THRESHOLD_0B:
+ aest_record_dbg(record, "do not support CE threshold!\n");
+ return;
+ case RAS_CE_THRESHOLD_8B:
+ aest_record_dbg(record, "support 8 bit CE threshold!\n");
+ break;
+ case RAS_CE_THRESHOLD_16B:
+ aest_record_dbg(record, "support 16 bit CE threshold!\n");
+ break;
+ case RAS_CE_THRESHOLD_32B:
+ aest_record_dbg(record, "not support 32 bit CE threshold!\n");
+ break;
+ default:
+ aest_record_dbg(record, "Unknown misc0 ce threshold!\n");
+ }
+
+ err_misc0 = record_read(record, ERXMISC0);
+ info = &ce_info[record->threshold_type];
+ ce->info = info;
+
+ // Default CE threshold is 1.
+ ce->count = info->max_count;
+ ce->threshold = DEFAULT_CE_THRESHOLD;
+ ce->reg_val = err_misc0 | info->mask;
+
+ record_write(record, ERXMISC0, ce->reg_val);
+ aest_record_dbg(record, "CE threshold is %llx, controlled by Kernel",
+ ce->threshold);
+}
+
static int get_aest_node_ver(struct aest_node *node)
{
u64 reg;
@@ -54,6 +127,7 @@ static int aest_init_record(struct aest_record *record, int i,
record->addressing_mode = test_bit(i, node->info->addressing_mode);
record->index = i;
record->node = node;
+ aest_set_ce_threshold(record);
aest_record_dbg(record, "base: %p, index: %d, address mode: %x\n",
record->regs_base, record->index,
diff --git a/drivers/ras/aest/aest.h b/drivers/ras/aest/aest.h
index 505ecd9635bc..85eeed79bcbe 100644
--- a/drivers/ras/aest/aest.h
+++ b/drivers/ras/aest/aest.h
@@ -9,6 +9,7 @@
#include <asm/ras.h>
#define MAX_GSI_PER_NODE 2
+#define DEFAULT_CE_THRESHOLD 1
#define record_read(record, offset) \
record->access->read(record->regs_base, offset)
@@ -71,6 +72,19 @@ struct aest_access {
void (*write)(void *base, u32 offset, u64 val);
};
+struct ce_threshold_info {
+ const u64 max_count;
+ const u64 mask;
+ const u64 shift;
+};
+
+struct ce_threshold {
+ const struct ce_threshold_info *info;
+ u64 count;
+ u64 threshold;
+ u64 reg_val;
+};
+
struct aest_record {
char *name;
int index;
@@ -88,6 +102,9 @@ struct aest_record {
int addressing_mode;
struct aest_node *node;
const struct aest_access *access;
+
+ struct ce_threshold ce;
+ enum ras_ce_threshold threshold_type;
};
struct aest_group {
diff --git a/include/linux/acpi_aest.h b/include/linux/acpi_aest.h
index 77187ce43d44..a7898c643896 100644
--- a/include/linux/acpi_aest.h
+++ b/include/linux/acpi_aest.h
@@ -13,6 +13,9 @@
/* AEST interrupt */
#define AEST_INTERRUPT_MODE BIT(0)
+#define AEST_INTERRUPT_FHI_UE_SUPPORT BIT(0)
+#define AEST_INTERRUPT_FHI_UE_NO_SUPPORT BIT(1)
+
#define AEST_MAX_INTERRUPT_PER_NODE 2
/* AEST interface */
--
2.51.2.612.gdc70283dfc
Powered by blists - more mailing lists