[PATCH 4/6] EDAC/altera: Add IO96B ECC support for Agilex5 SoCFPGA

niravkumarlaxmidas.rabara@altera.com posted 6 patches 3 months, 2 weeks ago
[PATCH 4/6] EDAC/altera: Add IO96B ECC support for Agilex5 SoCFPGA
Posted by niravkumarlaxmidas.rabara@altera.com 3 months, 2 weeks ago
From: Niravkumar L Rabara <niravkumarlaxmidas.rabara@altera.com>

Add EDAC driver support for the two IO96B memory controllers present in
Agilex5 SoCFPGA.

The IO96B controller provides dedicated mailbox registers for status
and control. ECC error injection is handled via Secure Monitor Calls (SMC).

Signed-off-by: Niravkumar L Rabara <niravkumarlaxmidas.rabara@altera.com>
---
 drivers/edac/Kconfig                         |  10 +
 drivers/edac/altera_edac.c                   | 305 ++++++++++++++++---
 drivers/edac/altera_edac.h                   |  42 +++
 include/linux/firmware/intel/stratix10-smc.h |  18 ++
 4 files changed, 326 insertions(+), 49 deletions(-)

diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index 39352b9b7a7e..33a9fccde2fe 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -410,6 +410,16 @@ config EDAC_ALTERA_SDRAM
 	  preloader must initialize the SDRAM before loading
 	  the kernel.
 
+config EDAC_ALTERA_IO96B
+	bool "Altera I096B ECC"
+	depends on EDAC_ALTERA=y && ARM64
+	help
+	  Support for SERR and DERR detection and correction on the
+	  IO96B memory controller interface for Altera SoCFPGA.
+
+	  I096B memory controller provides dedicated mailbox registers
+	  for error injection and error information.
+
 config EDAC_ALTERA_L2C
 	bool "Altera L2 Cache ECC"
 	depends on EDAC_ALTERA=y && CACHE_L2X0
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c
index ee3270bf75e6..a82c3b01be1a 100644
--- a/drivers/edac/altera_edac.c
+++ b/drivers/edac/altera_edac.c
@@ -656,6 +656,19 @@ static const struct file_operations altr_edac_a10_device_inject_fops __maybe_unu
 	.llseek = generic_file_llseek,
 };
 
+#ifdef CONFIG_EDAC_ALTERA_IO96B
+static ssize_t __maybe_unused
+altr_edac_io96b_device_trig(struct file *file, const char __user *user_buf,
+			    size_t count, loff_t *ppos);
+
+static const struct file_operations
+altr_edac_io96b_inject_fops __maybe_unused = {
+	.open = simple_open,
+	.write = altr_edac_io96b_device_trig,
+	.llseek = generic_file_llseek,
+};
+#endif
+
 static ssize_t __maybe_unused
 altr_edac_a10_device_trig2(struct file *file, const char __user *user_buf,
 			   size_t count, loff_t *ppos);
@@ -1121,6 +1134,33 @@ static const struct edac_device_prv_data s10_sdramecc_data = {
 };
 #endif /* CONFIG_EDAC_ALTERA_SDRAM */
 
+/************************IO96B EDAC *************************************/
+
+#ifdef CONFIG_EDAC_ALTERA_IO96B
+static DEFINE_MUTEX(io96b_mb_mutex);
+
+static int altr_agilex5_io96b_ecc_init(struct altr_edac_device_dev *device)
+{
+	u32 ecc_status;
+
+	ecc_status = readl(device->base + IO96B_ECC_ENABLE_INFO_OFST);
+	ecc_status &= GENMASK(1, 0);
+
+	if (!ecc_status) {
+		edac_printk(KERN_ERR, EDAC_DEVICE,
+			    "%s: No ECC present or ECC disabled.\n",
+			    device->edac_dev_name);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static const struct edac_device_prv_data agilex5_io96b_data = {
+	.setup = altr_agilex5_io96b_ecc_init,
+	.inject_fops = &altr_edac_io96b_inject_fops,
+};
+#endif /* CONFIG_EDAC_ALTERA_IO96B */
+
 /*********************** OCRAM EDAC Device Functions *********************/
 
 #ifdef CONFIG_EDAC_ALTERA_OCRAM
@@ -1717,11 +1757,62 @@ static const struct of_device_id altr_edac_a10_device_of_match[] = {
 #endif
 #ifdef CONFIG_EDAC_ALTERA_SDRAM
 	{ .compatible = "altr,sdram-edac-s10", .data = &s10_sdramecc_data },
+#endif
+#ifdef CONFIG_EDAC_ALTERA_IO96B
+	{ .compatible = "altr,socfpga-io96b0-ecc", .data = &agilex5_io96b_data },
+	{ .compatible = "altr,socfpga-io96b1-ecc", .data = &agilex5_io96b_data },
 #endif
 	{},
 };
 MODULE_DEVICE_TABLE(of, altr_edac_a10_device_of_match);
 
+/*
+ * The IO96B EDAC Device Functions differ from the rest of the
+ * ECC peripherals.
+ */
+
+#ifdef CONFIG_EDAC_ALTERA_IO96B
+static ssize_t __maybe_unused
+altr_edac_io96b_device_trig(struct file *file, const char __user *user_buf,
+			    size_t count, loff_t *ppos)
+{
+	struct edac_device_ctl_info *edac_dci = file->private_data;
+	struct altr_edac_device_dev *drvdata = edac_dci->pvt_info;
+	u8 trig_type;
+	u32 val;
+	struct arm_smccc_res result;
+
+	if (!user_buf || get_user(trig_type, user_buf))
+		return -EFAULT;
+
+	mutex_lock(&io96b_mb_mutex);
+	if (readl(drvdata->base + IO96B_CMD_RESP_STATUS_OFST))
+		writel(0, drvdata->base + IO96B_CMD_RESP_STATUS_OFST);
+
+	arm_smccc_smc(INTEL_SIP_SMC_IO96B_INJECT_ECC_ERR,
+		      (trig_type == ALTR_UE_TRIGGER_CHAR) ?
+		      IO96B_DBE_SYNDROME : IO96B_SBE_SYNDROME,
+		      IO96B_CMD_TRIG_ECC_ENJECT_OP, 0, 0, 0, 0, 0, &result);
+
+	writel(IO06B_ECC_SCRUB_INTERVAL, drvdata->base + IO96B_CMD_PARAM_0_OFST);
+	writel(IO06B_ECC_SCRUB_LEN, drvdata->base + IO96B_CMD_PARAM_1_OFST);
+	writel(IO06B_ECC_SCRUB_FULL_MEM, drvdata->base + IO96B_CMD_PARAM_2_OFST);
+	writel(IO96B_CMD_ECC_SCRUB_MODE_0, drvdata->base + IO96B_CMD_REQ_OFST);
+
+	if (readl_relaxed_poll_timeout(drvdata->base + IO96B_ECC_SCRUB_STAT0_OFST,
+				       val, !(val & IO96B_ECC_SCRUB_COMPLETE),
+				       IO96B_ECC_SCRUB_POLL_US,
+				       IO96B_ECC_SCRUB_TIMEOUT))
+		edac_printk(KERN_ALERT, EDAC_DEVICE,
+			    "IO96B ECC Scrubing timeout - Try again.\n");
+
+	writel(0, drvdata->base + IO96B_CMD_RESP_STATUS_OFST);
+	mutex_unlock(&io96b_mb_mutex);
+
+	return count;
+}
+#endif
+
 /*
  * The Arria10 EDAC Device Functions differ from the Cyclone5/Arria5
  * because 2 IRQs are shared among the all ECC peripherals. The ECC
@@ -1819,6 +1910,70 @@ altr_edac_a10_device_trig2(struct file *file, const char __user *user_buf,
 	return count;
 }
 
+static irqreturn_t io96b_irq_handler(int irq, void *dev_id)
+{
+	struct altr_edac_device_dev *dci = dev_id;
+	u32 err_word0;
+	u32 err_word1;
+	u32 cnt = 0;
+	u32 ecc_error_status;
+	u16 err_queue_overflow;
+	u16 err_count = 0;
+	bool dbe = false;
+	enum io96b_error_type error_type;
+	u32 err_queue = IO96B_ECC_ERR_ENTRIES_OFST;
+
+	ecc_error_status = readl(dci->base + IO96B_ECC_ERR_REG_OFST);
+	err_queue_overflow = ecc_error_status & GENMASK(31, 16);
+	err_count = ecc_error_status & GENMASK(15, 0);
+
+	if (!err_queue_overflow) {
+		while (cnt < err_count) {
+			err_word0 = readl(dci->base + err_queue);
+			err_word1 = readl(dci->base + (err_queue + 4));
+
+			error_type = (err_word0 & GENMASK(9, 6)) >> 6;
+			if (error_type == ECC_SINGLE_DBE || error_type == ECC_MULTI_DBE ||
+			    error_type == ECC_WRITE_LINK_DBE ||
+			    error_type == ECC_READ_LINK_DBE ||
+			    error_type == ECC_READ_LINK_RMW_DBE) {
+				edac_printk(KERN_ERR, EDAC_DEVICE,
+					    "%s: DBE: word0:0x%08X, word1:0x%08X\n",
+					    dci->edac_dev_name, err_word0, err_word1);
+				dbe = true;
+			} else {
+				edac_printk(KERN_ERR, EDAC_DEVICE,
+					    "%s: SBE: word0:0x%08X, word1:0x%08X\n",
+					    dci->edac_dev_name, err_word0, err_word1);
+				edac_device_handle_ce(dci->edac_dev, 0, 0,
+						      dci->edac_dev_name);
+			}
+			cnt++;
+			err_queue += 8;
+		}
+		if (dbe)
+			panic("\nEDAC:IO96B[Uncorrectable errors]\n");
+	} else {
+		err_queue_overflow = (err_word0 & GENMASK(9, 6)) >> 6;
+		if (error_type == ECC_SINGLE_DBE || error_type == ECC_MULTI_DBE ||
+		    error_type == ECC_WRITE_LINK_DBE ||
+		    error_type == ECC_READ_LINK_DBE ||
+		    error_type == ECC_READ_LINK_RMW_DBE) {
+			panic("\nEDAC: UE: %s: word0:0x%08X, word1:0x%08X\n",
+			      dci->edac_dev_name, err_word0, err_word1);
+		} else {
+			edac_printk(KERN_ERR, EDAC_DEVICE,
+				    "%s: Buffer Overflow SBE:0x%08X\n",
+				    dci->edac_dev_name, err_queue_overflow);
+			edac_device_handle_ce(dci->edac_dev, 0, 0, dci->edac_dev_name);
+		}
+	}
+
+	//Clear Queue
+	writel(IO96B_ECC_ERROR_QUEUE_CLEAR, dci->base + IO96B_CMD_REQ_OFST);
+	return IRQ_HANDLED;
+}
+
 static void altr_edac_a10_irq_handler(struct irq_desc *desc)
 {
 	int dberr, bit, sm_offset, irq_status;
@@ -1885,6 +2040,8 @@ static int altr_edac_a10_device_add(struct altr_arria10_edac *edac,
 	struct resource res;
 	int edac_idx;
 	int rc = 0;
+	bool io96b0_ecc = false;
+	bool io96b1_ecc = false;
 	const struct edac_device_prv_data *prv;
 	/* Get matching node and check for valid result */
 	const struct of_device_id *pdev_id =
@@ -1902,11 +2059,15 @@ static int altr_edac_a10_device_add(struct altr_arria10_edac *edac,
 
 	if (!devres_open_group(edac->dev, altr_edac_a10_device_add, GFP_KERNEL))
 		return -ENOMEM;
-
-	if (of_device_is_compatible(np, "altr,sdram-edac-s10"))
+	if (of_device_is_compatible(np, "altr,socfpga-io96b0-ecc")) {
+		io96b0_ecc = true;
+	} else if (of_device_is_compatible(np, "altr,socfpga-io96b1-ecc")) {
+		io96b1_ecc = true;
+	} else if (of_device_is_compatible(np, "altr,sdram-edac-s10")) {
 		rc = get_s10_sdram_edac_resource(np, &res);
-	else
+	} else {
 		rc = of_address_to_resource(np, 0, &res);
+	}
 
 	if (rc < 0) {
 		edac_printk(KERN_ERR, EDAC_DEVICE,
@@ -1938,10 +2099,22 @@ static int altr_edac_a10_device_add(struct altr_arria10_edac *edac,
 	dci->mod_name = ecc_name;
 	dci->dev_name = ecc_name;
 
-	altdev->base = devm_ioremap_resource(edac->dev, &res);
-	if (IS_ERR(altdev->base)) {
-		rc = PTR_ERR(altdev->base);
-		goto err_release_group1;
+	if (io96b0_ecc || io96b1_ecc) {
+		rc = of_address_to_resource(np, 0, &res);
+		if (rc)
+			goto err_release_group1;
+
+		altdev->base = ioremap(res.start, resource_size(&res));
+		if (IS_ERR(altdev->base)) {
+			rc = PTR_ERR(altdev->base);
+			goto err_release_group1;
+		}
+	} else {
+		altdev->base = devm_ioremap_resource(edac->dev, &res);
+		if (IS_ERR(altdev->base)) {
+			rc = PTR_ERR(altdev->base);
+			goto err_release_group1;
+		}
 	}
 
 	/* Check specific dependencies for the module */
@@ -1951,26 +2124,70 @@ static int altr_edac_a10_device_add(struct altr_arria10_edac *edac,
 			goto err_release_group1;
 	}
 
-	altdev->sb_irq = irq_of_parse_and_map(np, 0);
-	if (!altdev->sb_irq) {
-		edac_printk(KERN_ERR, EDAC_DEVICE, "Error allocating SBIRQ\n");
-		rc = -ENODEV;
-		goto err_release_group1;
-	}
-	rc = devm_request_irq(edac->dev, altdev->sb_irq, prv->ecc_irq_handler,
-			      IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
-			      ecc_name, altdev);
-	if (rc) {
-		edac_printk(KERN_ERR, EDAC_DEVICE, "No SBERR IRQ resource\n");
-		goto err_release_group1;
-	}
+	if (io96b0_ecc) {
+		altdev->io96b0_irq = altdev->edac->io96b0_irq;
+		rc = devm_request_threaded_irq(edac->dev, altdev->io96b0_irq, NULL,
+					       io96b_irq_handler, IRQF_ONESHOT,
+					       ecc_name, altdev);
+		if (rc) {
+			edac_printk(KERN_ERR, EDAC_DEVICE, "No IO96B0 IRQ resource\n");
+			goto err_release_group1;
+		}
+	} else if (io96b1_ecc) {
+		altdev->io96b1_irq = altdev->edac->io96b1_irq;
+		rc = devm_request_threaded_irq(edac->dev, altdev->io96b1_irq, NULL,
+					       io96b_irq_handler, IRQF_ONESHOT,
+					       ecc_name, altdev);
+		if (rc) {
+			edac_printk(KERN_ERR, EDAC_DEVICE, "No IO96B1 IRQ resource\n");
+			goto err_release_group1;
+		}
+	} else {
+		altdev->sb_irq = irq_of_parse_and_map(np, 0);
+		if (!altdev->sb_irq) {
+			edac_printk(KERN_ERR, EDAC_DEVICE, "Error allocating SBIRQ\n");
+			rc = -ENODEV;
+			goto err_release_group1;
+		}
+		rc = devm_request_irq(edac->dev, altdev->sb_irq, prv->ecc_irq_handler,
+				      IRQF_ONESHOT | IRQF_TRIGGER_HIGH, ecc_name, altdev);
+		if (rc) {
+			edac_printk(KERN_ERR, EDAC_DEVICE, "No SBERR IRQ resource\n");
+			goto err_release_group1;
+		}
 
 #ifdef CONFIG_64BIT
-	if (of_machine_is_compatible("intel,socfpga-agilex5")) {
+		if (of_machine_is_compatible("intel,socfpga-agilex5")) {
+			altdev->db_irq = irq_of_parse_and_map(np, 1);
+			if (!altdev->db_irq) {
+				edac_printk(KERN_ERR, EDAC_DEVICE,
+					    "Error allocating DBIRQ\n");
+				rc = -ENODEV;
+				goto err_release_group1;
+			}
+			rc = devm_request_irq(edac->dev, altdev->db_irq,
+					      prv->ecc_irq_handler,
+					      IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
+					      ecc_name, altdev);
+			if (rc) {
+				edac_printk(KERN_ERR, EDAC_DEVICE,
+					    "No DBERR IRQ resource\n");
+				goto err_release_group1;
+			}
+		} else {
+			/* Use IRQ to determine SError origin instead of assigning IRQ */
+			rc = of_property_read_u32_index(np, "interrupts", 0,
+							&altdev->db_irq);
+			if (rc) {
+				edac_printk(KERN_ERR, EDAC_DEVICE,
+					    "Unable to parse DB IRQ index\n");
+				goto err_release_group1;
+			}
+		}
+#else
 		altdev->db_irq = irq_of_parse_and_map(np, 1);
 		if (!altdev->db_irq) {
-			edac_printk(KERN_ERR, EDAC_DEVICE,
-				    "Error allocating DBIRQ\n");
+			edac_printk(KERN_ERR, EDAC_DEVICE, "Error allocating DBIRQ\n");
 			rc = -ENODEV;
 			goto err_release_group1;
 		}
@@ -1979,35 +2196,11 @@ static int altr_edac_a10_device_add(struct altr_arria10_edac *edac,
 				      IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
 				      ecc_name, altdev);
 		if (rc) {
-			edac_printk(KERN_ERR, EDAC_DEVICE,
-				    "No DBERR IRQ resource\n");
+			edac_printk(KERN_ERR, EDAC_DEVICE, "No DBERR IRQ resource\n");
 			goto err_release_group1;
 		}
-	} else {
-		/* Use IRQ to determine SError origin instead of assigning IRQ */
-		rc = of_property_read_u32_index(np, "interrupts", 0,
-						&altdev->db_irq);
-		if (rc) {
-			edac_printk(KERN_ERR, EDAC_DEVICE,
-				    "Unable to parse DB IRQ index\n");
-			goto err_release_group1;
-		}
-	}
-#else
-	altdev->db_irq = irq_of_parse_and_map(np, 1);
-	if (!altdev->db_irq) {
-		edac_printk(KERN_ERR, EDAC_DEVICE, "Error allocating DBIRQ\n");
-		rc = -ENODEV;
-		goto err_release_group1;
-	}
-	rc = devm_request_irq(edac->dev, altdev->db_irq, prv->ecc_irq_handler,
-			      IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
-			      ecc_name, altdev);
-	if (rc) {
-		edac_printk(KERN_ERR, EDAC_DEVICE, "No DBERR IRQ resource\n");
-		goto err_release_group1;
-	}
 #endif
+	}
 
 	rc = edac_device_add_device(dci);
 	if (rc) {
@@ -2198,7 +2391,21 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
 			regmap_write(edac->ecc_mgr_map,
 				     S10_SYSMGR_UE_ADDR_OFST, 0);
 		}
+
+#ifdef CONFIG_EDAC_ALTERA_IO96B
+		edac->io96b0_irq = platform_get_irq_byname(pdev, "io96b0");
+		if (edac->io96b0_irq < 0) {
+			dev_err(&pdev->dev, "No io96b0 IRQ resource\n");
+			return edac->io96b0_irq;
+		}
+		edac->io96b1_irq = platform_get_irq_byname(pdev, "io96b1");
+		if (edac->io96b1_irq < 0) {
+			dev_err(&pdev->dev, "No io96b1 IRQ resource\n");
+			return edac->io96b1_irq;
+		}
+#endif
 	}
+
 #else
 	edac->db_irq = platform_get_irq(pdev, 1);
 	if (edac->db_irq < 0)
diff --git a/drivers/edac/altera_edac.h b/drivers/edac/altera_edac.h
index 7248d24c4908..a2c8b80eefa8 100644
--- a/drivers/edac/altera_edac.h
+++ b/drivers/edac/altera_edac.h
@@ -352,6 +352,44 @@ struct altr_sdram_mc_data {
 #define ECC_READ_EOVR                     0x2
 #define ECC_READ_EDOVR                    0x3
 
+/************ IO96B ECC defines *******/
+#define IO96B_ECC_ENABLE_INFO_OFST		0x240
+#define IO96B_ECC_SCRUB_STAT0_OFST		0x244
+#define IO96B_ECC_ERR_REG_OFST			0x300
+#define IO96B_ECC_ERR_ENTRIES_OFST		0x310
+
+#define IO96B_CMD_RESP_STATUS_OFST		0x45C
+#define IO96B_CMD_RESP_DATA_0_OFST		0x458
+#define IO96B_CMD_RESP_DATA_1_OFST		0x454
+#define IO96B_CMD_RESP_DATA_2_OFST		0x450
+#define IO96B_CMD_REQ_OFST			0x43C
+#define IO96B_CMD_PARAM_0_OFST			0x438
+#define IO96B_CMD_PARAM_1_OFST			0x434
+#define IO96B_CMD_PARAM_2_OFST			0x430
+
+#define IO96B_CMD_TRIG_ECC_ENJECT_OP		0x20040109
+#define IO96B_CMD_ECC_SCRUB_MODE_0		0x20040202
+#define IO96B_ECC_ERROR_QUEUE_CLEAR		0x20040110
+
+#define IO06B_ECC_SCRUB_INTERVAL		0x14
+#define IO06B_ECC_SCRUB_LEN			0x100
+#define IO06B_ECC_SCRUB_FULL_MEM		0x1
+
+#define IO96B_SBE_SYNDROME			0xF4
+#define IO96B_DBE_SYNDROME			0xFF
+
+#define IO96B_ECC_SCRUB_TIMEOUT		400000
+#define IO96B_ECC_SCRUB_POLL_US		500
+#define IO96B_ECC_SCRUB_COMPLETE		BIT(1)
+
+enum io96b_error_type {
+	ECC_SINGLE_DBE = 2,
+	ECC_MULTI_DBE = 3,
+	ECC_WRITE_LINK_DBE = 0xa,
+	ECC_READ_LINK_DBE = 0xc,
+	ECC_READ_LINK_RMW_DBE
+};
+
 struct altr_edac_device_dev;
 
 struct edac_device_prv_data {
@@ -384,6 +422,8 @@ struct altr_edac_device_dev {
 	struct edac_device_ctl_info *edac_dev;
 	struct device ddev;
 	int edac_idx;
+	int io96b0_irq;
+	int io96b1_irq;
 };
 
 struct altr_arria10_edac {
@@ -395,6 +435,8 @@ struct altr_arria10_edac {
 	struct irq_chip		irq_chip;
 	struct list_head	a10_ecc_devices;
 	struct notifier_block	panic_notifier;
+	int io96b0_irq;
+	int io96b1_irq;
 };
 
 #endif	/* #ifndef _ALTERA_EDAC_H */
diff --git a/include/linux/firmware/intel/stratix10-smc.h b/include/linux/firmware/intel/stratix10-smc.h
index ee80ca4bb0d0..283597022e61 100644
--- a/include/linux/firmware/intel/stratix10-smc.h
+++ b/include/linux/firmware/intel/stratix10-smc.h
@@ -620,4 +620,22 @@ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_COMPLETED_WRITE)
 #define INTEL_SIP_SMC_FCS_GET_PROVISION_DATA \
 	INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FCS_GET_PROVISION_DATA)
 
+/**
+ * Request INTEL_SIP_SMC_IO96B_INJECT_ECC_ERR
+ * Sync call to inject IO96B ECC Error.
+ *
+ * Call register usage:
+ * a0 INTEL_SIP_SMC_FUNCID_IO96B_INJECT_ECC_ERR
+ * a1 IO96B error syndrome
+ * a2 I096B mailbox command
+ *
+ * Return status:
+ * a0 INTEL_SIP_SMC_STATUS_OK, INTEL_SIP_SMC_STATUS_NOT_SUPPORTED or
+ *    INTEL_SIP_SMC_STATUS_ERROR
+ * a1-a3 Not used
+ */
+#define INTEL_SIP_SMC_FUNCID_IO96B_INJECT_ECC_ERR 156
+#define INTEL_SIP_SMC_IO96B_INJECT_ECC_ERR \
+		INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_IO96B_INJECT_ECC_ERR)
+
 #endif
-- 
2.25.1
Re: [PATCH 4/6] EDAC/altera: Add IO96B ECC support for Agilex5 SoCFPGA
Posted by kernel test robot 3 months, 1 week ago
Hi,

kernel test robot noticed the following build warnings:

[auto build test WARNING on ras/edac-for-next]
[also build test WARNING on robh/for-next linus/master v6.18-rc4 next-20251103]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/niravkumarlaxmidas-rabara-altera-com/dt-bindings-edac-altera-Document-additional-ECC-instances/20251028-173250
base:   https://git.kernel.org/pub/scm/linux/kernel/git/ras/ras.git edac-for-next
patch link:    https://lore.kernel.org/r/20251028092232.773991-5-niravkumarlaxmidas.rabara%40altera.com
patch subject: [PATCH 4/6] EDAC/altera: Add IO96B ECC support for Agilex5 SoCFPGA
config: arm64-allyesconfig (https://download.01.org/0day-ci/archive/20251104/202511041157.q0be68K1-lkp@intel.com/config)
compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project d2625a438020ad35330cda29c3def102c1687b1b)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251104/202511041157.q0be68K1-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202511041157.q0be68K1-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/edac/altera_edac.c:1957:25: warning: variable 'err_word0' is uninitialized when used here [-Wuninitialized]
    1957 |                 err_queue_overflow = (err_word0 & GENMASK(9, 6)) >> 6;
         |                                       ^~~~~~~~~
   drivers/edac/altera_edac.c:1916:15: note: initialize the variable 'err_word0' to silence this warning
    1916 |         u32 err_word0;
         |                      ^
         |                       = 0
>> drivers/edac/altera_edac.c:1963:41: warning: variable 'err_word1' is uninitialized when used here [-Wuninitialized]
    1963 |                               dci->edac_dev_name, err_word0, err_word1);
         |                                                              ^~~~~~~~~
   drivers/edac/altera_edac.c:1917:15: note: initialize the variable 'err_word1' to silence this warning
    1917 |         u32 err_word1;
         |                      ^
         |                       = 0
>> drivers/edac/altera_edac.c:1958:7: warning: variable 'error_type' is uninitialized when used here [-Wuninitialized]
    1958 |                 if (error_type == ECC_SINGLE_DBE || error_type == ECC_MULTI_DBE ||
         |                     ^~~~~~~~~~
   drivers/edac/altera_edac.c:1923:2: note: variable 'error_type' is declared here
    1923 |         enum io96b_error_type error_type;
         |         ^
   3 warnings generated.


vim +/err_word0 +1957 drivers/edac/altera_edac.c

  1912	
  1913	static irqreturn_t io96b_irq_handler(int irq, void *dev_id)
  1914	{
  1915		struct altr_edac_device_dev *dci = dev_id;
  1916		u32 err_word0;
  1917		u32 err_word1;
  1918		u32 cnt = 0;
  1919		u32 ecc_error_status;
  1920		u16 err_queue_overflow;
  1921		u16 err_count = 0;
  1922		bool dbe = false;
  1923		enum io96b_error_type error_type;
  1924		u32 err_queue = IO96B_ECC_ERR_ENTRIES_OFST;
  1925	
  1926		ecc_error_status = readl(dci->base + IO96B_ECC_ERR_REG_OFST);
  1927		err_queue_overflow = ecc_error_status & GENMASK(31, 16);
  1928		err_count = ecc_error_status & GENMASK(15, 0);
  1929	
  1930		if (!err_queue_overflow) {
  1931			while (cnt < err_count) {
  1932				err_word0 = readl(dci->base + err_queue);
  1933				err_word1 = readl(dci->base + (err_queue + 4));
  1934	
  1935				error_type = (err_word0 & GENMASK(9, 6)) >> 6;
  1936				if (error_type == ECC_SINGLE_DBE || error_type == ECC_MULTI_DBE ||
  1937				    error_type == ECC_WRITE_LINK_DBE ||
  1938				    error_type == ECC_READ_LINK_DBE ||
  1939				    error_type == ECC_READ_LINK_RMW_DBE) {
  1940					edac_printk(KERN_ERR, EDAC_DEVICE,
  1941						    "%s: DBE: word0:0x%08X, word1:0x%08X\n",
  1942						    dci->edac_dev_name, err_word0, err_word1);
  1943					dbe = true;
  1944				} else {
  1945					edac_printk(KERN_ERR, EDAC_DEVICE,
  1946						    "%s: SBE: word0:0x%08X, word1:0x%08X\n",
  1947						    dci->edac_dev_name, err_word0, err_word1);
  1948					edac_device_handle_ce(dci->edac_dev, 0, 0,
  1949							      dci->edac_dev_name);
  1950				}
  1951				cnt++;
  1952				err_queue += 8;
  1953			}
  1954			if (dbe)
  1955				panic("\nEDAC:IO96B[Uncorrectable errors]\n");
  1956		} else {
> 1957			err_queue_overflow = (err_word0 & GENMASK(9, 6)) >> 6;
> 1958			if (error_type == ECC_SINGLE_DBE || error_type == ECC_MULTI_DBE ||
  1959			    error_type == ECC_WRITE_LINK_DBE ||
  1960			    error_type == ECC_READ_LINK_DBE ||
  1961			    error_type == ECC_READ_LINK_RMW_DBE) {
  1962				panic("\nEDAC: UE: %s: word0:0x%08X, word1:0x%08X\n",
> 1963				      dci->edac_dev_name, err_word0, err_word1);
  1964			} else {
  1965				edac_printk(KERN_ERR, EDAC_DEVICE,
  1966					    "%s: Buffer Overflow SBE:0x%08X\n",
  1967					    dci->edac_dev_name, err_queue_overflow);
  1968				edac_device_handle_ce(dci->edac_dev, 0, 0, dci->edac_dev_name);
  1969			}
  1970		}
  1971	
  1972		//Clear Queue
  1973		writel(IO96B_ECC_ERROR_QUEUE_CLEAR, dci->base + IO96B_CMD_REQ_OFST);
  1974		return IRQ_HANDLED;
  1975	}
  1976	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH 4/6] EDAC/altera: Add IO96B ECC support for Agilex5 SoCFPGA
Posted by Borislav Petkov 3 months, 1 week ago
On Tue, Oct 28, 2025 at 05:22:30PM +0800, niravkumarlaxmidas.rabara@altera.com wrote:
> diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
> index 39352b9b7a7e..33a9fccde2fe 100644
> --- a/drivers/edac/Kconfig
> +++ b/drivers/edac/Kconfig
> @@ -410,6 +410,16 @@ config EDAC_ALTERA_SDRAM
>  	  preloader must initialize the SDRAM before loading
>  	  the kernel.
>  
> +config EDAC_ALTERA_IO96B
> +	bool "Altera I096B ECC"

Is this and the other new Kconfig symbols you're adding absolutely needed?

IOW, why can't the driver simply load on that new hw without needing Kconfig
symbols at all?

What are they really saving?

Thx.

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
Re: [PATCH 4/6] EDAC/altera: Add IO96B ECC support for Agilex5 SoCFPGA
Posted by Niravkumar L Rabara 3 months, 1 week ago

On 30/10/2025 10:30 pm, Borislav Petkov wrote:
> On Tue, Oct 28, 2025 at 05:22:30PM +0800,niravkumarlaxmidas.rabara@altera.com wrote:
>> diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
>> index 39352b9b7a7e..33a9fccde2fe 100644
>> --- a/drivers/edac/Kconfig
>> +++ b/drivers/edac/Kconfig
>> @@ -410,6 +410,16 @@ config EDAC_ALTERA_SDRAM
>>   	  preloader must initialize the SDRAM before loading
>>   	  the kernel.
>>   
>> +config EDAC_ALTERA_IO96B
>> +	bool "Altera I096B ECC"
> Is this and the other new Kconfig symbols you're adding absolutely needed?
> 
> IOW, why can't the driver simply load on that new hw without needing Kconfig
> symbols at all?
> 
> What are they really saving?
> 
> Thx.
> 
> -- Regards/Gruss, Boris.


Hi Boris,

Thanks for your review.
Your point is absolutely valid — I was initially hesitant to introduce a 
different flow in the common altera_edac.c driver, so I followed the 
existing architecture where each ECC device has its own Kconfig entry 
and corresponding #ifdef blocks in the code.

In terms of savings, this approach mainly avoids compiling code based on 
Kconfig selection.

If the preferred approach is to detect and handle the new hardware 
without adding a separate Kconfig symbol, I’ll revise the patch accordingly.

In next version, I also need to address Krzysztof’s review comments 
regarding the introduction of new ECC device names in the DT bindings.

Thanks,
Nirav




Re: [PATCH 4/6] EDAC/altera: Add IO96B ECC support for Agilex5 SoCFPGA
Posted by Borislav Petkov 3 months, 1 week ago
On Fri, Oct 31, 2025 at 07:52:41PM +0800, Niravkumar L Rabara wrote:
> In terms of savings, this approach mainly avoids compiling code based on
> Kconfig selection.

That would be the main argument which determines whether those new Kconfig
options are needed. Read: the kernel has waaaay tooo many Kconfig options so
if not absolutely necessary, do not add more pls.

Thx.

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette