Linux EDAC development
 help / color / mirror / Atom feed
* [PATCH v6 0/3] Add Renesas RZ/N1x EDAC driver
@ 2026-10-13 10:44 Paul Louvel
  2026-10-13 10:44 ` [PATCH v6 1/3] dt-bindings: edac: cdns,ddr-edac: add Cadence DDR EDAC Paul Louvel
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Paul Louvel @ 2026-10-13 10:44 UTC (permalink / raw)
  To: Borislav Petkov, Tony Luck, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Geert Uytterhoeven, Magnus Damm
  Cc: linux-kernel, linux-renesas-soc, linux-edac, devicetree,
	Thomas Petazzoni, Miquel Raynal, Herve Codina,
	Paul Louvel (Schneider Electric), Wolfram Sang

The Cadence memory controller found on Renesas RZ/N1x SoCs supports ECC
with SECDED.

The memory controller found on the r9a06g032 supports at most a single
DIMM of DDR2/3, up to 2GB.

Add the EDAC driver for this memory controller, and the relevant
device-tree binding. Also add the EDAC node to the existing r9a06g032
SoC base device-tree.

Signed-off-by: Paul Louvel (Schneider Electric) <paul.louvel@bootlin.com>
---
Changes in v6:

- Patch 2:

  - Sorted the includes alphabetically. I have also ran a check with
    IWYU and, after careful checking, there is no superfluous header.

  - cdns_get_mem_sz() helper inlined into cnds_setup_mc().

  - Debugfs helpers built unconditionally with no-op stubs if debugfs is
    not selected.

  - Port command channel error reporting removed since it does not
    belong to an EDAC driver.

  - Removed useless comments.

  - Renamed macro prefix to CDNS_* instead of CDNS_DDR_*. Also indented
    all the macros definition at the same level.

  - Dropped cnds_ prefix to the private structure.

  - Shortened interrupt unmasking into a single macro to shorten line
    column length.

  - OR'ing flags to detect type of errors in irq handler.

- Link to v5: https://patch.msgid.link/20261005-paul-v7-3-rc1-edac-v5-0-140a0f124bc0@bootlin.com

Changes in v5:

- Patch 1:

    - Reworded commit title.

    - Dropped unused label.

    - Switch to plain flow scalar for description.

    Note: keeping cdns,ddr-edac compatible after Krzysztof's comment.

- Patch 2:

    - Setup IRQ after allocating and registering the device to EDAC
      core.

    - Explicitly free the interrupt line with devm_free_irq() to avoid
      any UAF before releasing MC.

- Link to v4: https://patch.msgid.link/20260928-paul-v7-3-rc1-edac-v4-0-4646a5b7cac9@bootlin.com

Changes in v4:
- Patch 1 and 3: drop the RZN1 family compatible, as requested by Geert.
- Patch 2:

    - Instead of using FIELD_MODIFY macro that expect a compile-time
      constant mask, use binary operators instead. Note that the new
      code is doing exactly what FIELD_MODIFY do, minus the error if
      cdns_rmw() is not inlined.

    - Use a spinlock instead of mutex since the locked region will be
      really fast to execute. Only lock it once for two register rmw.

    - Use edac_debugfs_* facility instead of sysfs attributes. Use a
      separate "bits" and "inject" file instead of just a single file.
      This is better since clients can read the previously set syndrome.

    - Cleaned up the probe function in separate functions. Use a remove
      function instead of registering custom devm action. Use a goto for
      resources cleaning.

    - Mask all interrupts before un-registering the memory controller.

- Link to v3: https://patch.msgid.link/20260924-paul-v7-3-rc1-edac-v3-0-bd8054a5b180@bootlin.com

Changes in v3:
- Patch 2:

    - Added trailing newline to end message.

    - Forgot mutex_init() in probe...

    - Registering the MC at the end of the probe.

- Link to v2: https://patch.msgid.link/20260924-paul-v7-3-rc1-edac-v2-0-bc1406161ecc@bootlin.com

Changes in v2:
- Patch 2:

    - FIELD_MODIFY() is always called with a compile-time constant mask
      in this driver. I see no problem here.

    - Use devm_clk_get_enabled() so the driver does not need to store
      struct clk_bulk_data outside the stack space.
      Clocks are not manipulated outside of probe.

    - Introduced a mutex for rmw operations in inject_ctrl_store() sysfs
      callback. Rename drv to priv in cdns_rmw().

    - Return IRQ_NONE if the stat int register is empty.

    - I see no reason to follow Sashiko last remark on this patch.
      Is an unhandled interrupt a big deal in this case ?

- Link to v1: https://patch.msgid.link/20260924-paul-v7-3-rc1-edac-v1-0-70be37c41a18@bootlin.com

---
Paul Louvel (3):
      dt-bindings: edac: cdns,ddr-edac: add Cadence DDR EDAC
      EDAC/cadence: Add Cadence DDR EDAC driver
      ARM: dts: renesas: r9a06g032: add EDAC node

 .../devicetree/bindings/edac/cdns,ddr-edac.yaml    |  61 ++++
 MAINTAINERS                                        |   7 +
 arch/arm/boot/dts/renesas/r9a06g032.dtsi           |   9 +
 drivers/edac/Kconfig                               |  10 +
 drivers/edac/Makefile                              |   1 +
 drivers/edac/cadence_edac.c                        | 377 +++++++++++++++++++++
 6 files changed, 465 insertions(+)
---
base-commit: d9f39b2c0579f313d14954a9ec584511c543aab5
change-id: 20260916-paul-v7-3-rc1-edac-1f479f96fc57

Best regards,
--  
Paul Louvel, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v6 1/3] dt-bindings: edac: cdns,ddr-edac: add Cadence DDR EDAC
  2026-10-13 10:44 [PATCH v6 0/3] Add Renesas RZ/N1x EDAC driver Paul Louvel
@ 2026-10-13 10:44 ` Paul Louvel
  2026-10-13 10:44 ` [PATCH v6 2/3] EDAC/cadence: Add Cadence DDR EDAC driver Paul Louvel
  2026-10-13 10:45 ` [PATCH v6 3/3] ARM: dts: renesas: r9a06g032: add EDAC node Paul Louvel
  2 siblings, 0 replies; 8+ messages in thread
From: Paul Louvel @ 2026-10-13 10:44 UTC (permalink / raw)
  To: Borislav Petkov, Tony Luck, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Geert Uytterhoeven, Magnus Damm
  Cc: linux-kernel, linux-renesas-soc, linux-edac, devicetree,
	Thomas Petazzoni, Miquel Raynal, Herve Codina,
	Paul Louvel (Schneider Electric), Wolfram Sang

Add the Cadence EDAC dt binding.
The memory controller optionally supports ECC SECDED with DDR2/DDR3
memory.

Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Paul Louvel (Schneider Electric) <paul.louvel@bootlin.com>
---
 .../devicetree/bindings/edac/cdns,ddr-edac.yaml    | 61 ++++++++++++++++++++++
 MAINTAINERS                                        |  6 +++
 2 files changed, 67 insertions(+)

diff --git a/Documentation/devicetree/bindings/edac/cdns,ddr-edac.yaml b/Documentation/devicetree/bindings/edac/cdns,ddr-edac.yaml
new file mode 100644
index 000000000000..9414cb9c59b9
--- /dev/null
+++ b/Documentation/devicetree/bindings/edac/cdns,ddr-edac.yaml
@@ -0,0 +1,61 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/edac/cdns,ddr-edac.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Cadence DDR EDAC
+
+maintainers:
+  - Paul Louvel <paul.louvel@bootlin.com>
+
+description:
+  The Cadence DDR supports DDR2 and DDR3 memory with or without ECC.
+  The bootloader must configure ECC mode in the memory controller.
+
+  The memory controller supports SECDED (single bit error correction, double bit
+  error detection). ECC scrubbing has to be done via software.
+
+properties:
+  compatible:
+    items:
+      - const: renesas,r9a06g032-ddr-edac # RZ/N1D
+      - const: cdns,ddr-edac
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: DDR controller clock
+      - description: APB internal bus clock
+
+  clock-names:
+    items:
+      - const: ddrc
+      - const: pclk
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/r9a06g032-sysctrl.h>
+
+    memory-controller@4000d000 {
+      compatible = "renesas,r9a06g032-ddr-edac", "cdns,ddr-edac";
+      reg = <0x4000d000 0x1000>;
+      interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
+      clocks = <&sysctrl R9A06G032_CLK_DDRC>, <&sysctrl R9A06G032_HCLK_DDRC>;
+      clock-names = "ddrc", "pclk";
+    };
diff --git a/MAINTAINERS b/MAINTAINERS
index 3a19da74d00c..60db3734df5b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5760,6 +5760,12 @@ L:	linux-mm@kvack.org
 S:	Maintained
 F:	tools/testing/selftests/cachestat/test_cachestat.c
 
+CADENCE DDR EDAC DRIVER
+M:	Paul Louvel <paul.louvel@bootlin.com>
+L:	linux-edac@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/edac/cdns,ddr-edac.yaml
+
 CADENCE MIPI-CSI2 BRIDGES
 M:	Maxime Ripard <mripard@kernel.org>
 L:	linux-media@vger.kernel.org

-- 
2.56.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH v6 2/3] EDAC/cadence: Add Cadence DDR EDAC driver
  2026-10-13 10:44 [PATCH v6 0/3] Add Renesas RZ/N1x EDAC driver Paul Louvel
  2026-10-13 10:44 ` [PATCH v6 1/3] dt-bindings: edac: cdns,ddr-edac: add Cadence DDR EDAC Paul Louvel
@ 2026-10-13 10:44 ` Paul Louvel
  2026-10-13 10:45 ` [PATCH v6 3/3] ARM: dts: renesas: r9a06g032: add EDAC node Paul Louvel
  2 siblings, 0 replies; 8+ messages in thread
From: Paul Louvel @ 2026-10-13 10:44 UTC (permalink / raw)
  To: Borislav Petkov, Tony Luck, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Geert Uytterhoeven, Magnus Damm
  Cc: linux-kernel, linux-renesas-soc, linux-edac, devicetree,
	Thomas Petazzoni, Miquel Raynal, Herve Codina,
	Paul Louvel (Schneider Electric)

Add the Cadence EDAC driver found on Renesas RZ/N1x SoC.
The memory controller supports ECC, software scrubbing, and SECDED.

Signed-off-by: Paul Louvel (Schneider Electric) <paul.louvel@bootlin.com>
---
 MAINTAINERS                 |   1 +
 drivers/edac/Kconfig        |  10 ++
 drivers/edac/Makefile       |   1 +
 drivers/edac/cadence_edac.c | 377 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 389 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 60db3734df5b..0c7ab1d22172 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5765,6 +5765,7 @@ M:	Paul Louvel <paul.louvel@bootlin.com>
 L:	linux-edac@vger.kernel.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/edac/cdns,ddr-edac.yaml
+F:	drivers/edac/cadence_edac.c
 
 CADENCE MIPI-CSI2 BRIDGES
 M:	Maxime Ripard <mripard@kernel.org>
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index a44b85c440ca..1707fe76b53c 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -503,6 +503,16 @@ config EDAC_QCOM
 	  For debugging issues having to do with stability and overall system
 	  health, you should probably say 'Y' here.
 
+config EDAC_CADENCE
+	tristate "Cadence EDAC Controller"
+	depends on HAS_IOMEM && OF
+	depends on ARCH_RZN1 || COMPILE_TEST
+	help
+	  Support for error detection and correction on RZN1x SoCs. ECC must be
+	  configured by the bootloader.
+	  The controller supports single bit error correction, double bit error
+	  detection.
+
 config EDAC_ASPEED
 	tristate "Aspeed AST BMC SoC"
 	depends on ARCH_ASPEED
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index a37534300ab9..04ab4d038406 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -91,3 +91,4 @@ obj-$(CONFIG_EDAC_VERSAL)		+= versal_edac.o
 obj-$(CONFIG_EDAC_LOONGSON)		+= loongson_edac.o
 obj-$(CONFIG_EDAC_VERSALNET)		+= versalnet_edac.o
 obj-$(CONFIG_EDAC_CORTEX_A72)		+= a72_edac.o
+obj-$(CONFIG_EDAC_CADENCE)		+= cadence_edac.o
diff --git a/drivers/edac/cadence_edac.c b/drivers/edac/cadence_edac.c
new file mode 100644
index 000000000000..1c0e3bcbec9f
--- /dev/null
+++ b/drivers/edac/cadence_edac.c
@@ -0,0 +1,377 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2015 Renesas Electronics Europe Ltd.
+ * Copyright 2026 Bootlin
+ *
+ * Based on highbank EDAC driver:
+ *
+ * Copyright 2011-2012 Calxeda, Inc.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/edac.h>
+#include <linux/interrupt.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+
+#include "edac_mc.h"
+#include "edac_module.h"
+
+#define DRV_NAME				"cadence_edac"
+
+#define REG_BYTE_SZ				4
+#define DDR_CTL(n)				((n) * REG_BYTE_SZ)
+
+#define CDNS_DDR_STAT				DDR_CTL(0)
+#define CDNS_DDR_STAT_DRAM_CLASS		GENMASK_U32(11, 8)
+#define CDNS_DDR_STAT_DRAM_DDR2			BIT(2)
+#define CDNS_DDR_STAT_GET_DRAM_CLASS(reg)	FIELD_GET(CDNS_DDR_STAT_DRAM_CLASS, reg)
+
+#define CDNS_ECC_STAT				DDR_CTL(36)
+#define CDNS_ECC_STAT_ENABLED			BIT(16)
+#define CDNS_ECC_STAT_IS_ENABLED(reg)		FIELD_GET(CDNS_ECC_STAT_ENABLED, reg)
+#define CDNS_ECC_STAT_FWC			BIT(24)
+
+#define CDNS_ECC_XOR				DDR_CTL(37)
+#define CDNS_ECC_XOR_CHECK_BITS			GENMASK_U32(13, 0)
+
+#define CDNS_BUS_CTRL				DDR_CTL(54)
+#define CDNS_BUS_CTRL_REDUC			BIT(1)
+
+/* DDR Controller Error Registers */
+
+#define CDNS_ECC_U_ERR_ADDR			DDR_CTL(38)
+#define CDNS_ECC_U_ERR_STAT			DDR_CTL(39)
+
+#define CDNS_ECC_C_ERR_ADDR			DDR_CTL(41)
+#define CDNS_ECC_C_ERR_STAT			DDR_CTL(42)
+
+#define CDNS_ECC_ERR_STAT_GET_SYNDROME(reg)	FIELD_GET(GENMASK(6, 0), reg)
+
+#define CDNS_PORT_CMD_ERR_ADDR			DDR_CTL(61)
+#define CDNS_PORT_CMD_ERR_TYPE			DDR_CTL(62)
+#define CDNS_PORT_CMD_ERR_TYPE_GET(reg)		FIELD_GET(GENMASK_U32(10, 8), reg)
+
+/* DDR Controller Interrupt Registers */
+
+#define CDNS_ECC_INT_STAT			DDR_CTL(56)
+#define CDNS_ECC_INT_STAT_CE			BIT(3)
+#define CDNS_ECC_INT_STAT_MULTIPLE_CE		BIT(4)
+#define CDNS_ECC_INT_STAT_UE			BIT(5)
+#define CDNS_ECC_INT_STAT_MULTIPLE_UE		BIT(6)
+#define CDNS_ECC_INT_EDAC			(CDNS_ECC_INT_STAT_CE | \
+						 CDNS_ECC_INT_STAT_MULTIPLE_CE | \
+						 CDNS_ECC_INT_STAT_UE | \
+						 CDNS_ECC_INT_STAT_MULTIPLE_UE)
+
+#define CDNS_ECC_INT_ACK			DDR_CTL(57)
+#define CDNS_ECC_INT_ACK_MASK			GENMASK_U32(21, 0)
+
+#define CDNS_ECC_INT_CTRL			DDR_CTL(58)
+#define CDNS_ECC_INT_CTRL_MASK			GENMASK_U32(21, 0)
+#define CDNS_ECC_INT_CTRL_MASK_ALL		BIT(22)
+#define CDNS_ECC_INT_CTRL_UNMASK(i)		((~(i)) & CDNS_ECC_INT_CTRL_MASK)
+
+struct mc_priv {
+	int irq;
+	void __iomem *io_base;
+	struct dentry *debugfs;
+	spinlock_t lock;
+	u16 xor_check_bits;
+};
+
+static irqreturn_t cdns_mc_err_handler(int irq, void *dev_id)
+{
+	struct mem_ctl_info *mci = dev_id;
+	struct mc_priv *priv = mci->pvt_info;
+	u32 status, err_addr, syndrome, reg;
+
+	status = readl(priv->io_base + CDNS_ECC_INT_STAT);
+	if (!status)
+		return IRQ_NONE;
+
+	/*
+	 * Hardware is not exposing how many CE / UE occurred since last ACK in
+	 * case of multiple errors. Always set the error count to 1.
+	 */
+
+	if (status & (CDNS_ECC_INT_STAT_UE | CDNS_ECC_INT_STAT_MULTIPLE_UE)) {
+		reg = readl(priv->io_base + CDNS_ECC_U_ERR_STAT);
+		syndrome = CDNS_ECC_ERR_STAT_GET_SYNDROME(reg);
+
+		err_addr = readl(priv->io_base + CDNS_ECC_U_ERR_ADDR);
+
+		edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, err_addr >> PAGE_SHIFT,
+				     err_addr & ~PAGE_MASK, syndrome, 0, 0, -1, mci->ctl_name, "");
+	}
+
+	if (status & (CDNS_ECC_INT_STAT_CE | CDNS_ECC_INT_STAT_MULTIPLE_CE)) {
+		reg = readl(priv->io_base + CDNS_ECC_C_ERR_STAT);
+		syndrome = CDNS_ECC_ERR_STAT_GET_SYNDROME(reg);
+
+		err_addr = readl(priv->io_base + CDNS_ECC_C_ERR_ADDR);
+
+		edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, err_addr >> PAGE_SHIFT,
+				     err_addr & ~PAGE_MASK, syndrome, 0, 0, -1, mci->ctl_name, "");
+	}
+
+	writel(status & CDNS_ECC_INT_ACK_MASK, priv->io_base + CDNS_ECC_INT_ACK);
+
+	return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_EDAC_DEBUG
+
+static void cdns_rmw(struct mc_priv *priv, u32 reg, u32 mask, u32 val)
+{
+	u32 regval;
+
+	regval = readl(priv->io_base + reg);
+	regval &= ~mask;
+	regval |= (val << __bf_shf(mask)) & mask;
+	writel(regval, priv->io_base + reg);
+}
+
+static ssize_t cdns_force_ecc_error(struct file *file, const char __user *data, size_t count,
+				    loff_t *ppos)
+{
+	struct device *dev = file->private_data;
+	struct mem_ctl_info *mci = to_mci(dev);
+	struct mc_priv *priv = mci->pvt_info;
+
+	spin_lock(&priv->lock);
+
+	cdns_rmw(priv, CDNS_ECC_XOR, CDNS_ECC_XOR_CHECK_BITS, priv->xor_check_bits);
+	cdns_rmw(priv, CDNS_ECC_STAT, CDNS_ECC_STAT_FWC, 1);
+
+	spin_unlock(&priv->lock);
+
+	return count;
+}
+
+static const struct file_operations cdns_ecc_error_fops = {
+	.open = simple_open,
+	.write = cdns_force_ecc_error,
+	.llseek = generic_file_llseek,
+};
+
+static void cdns_setup_debugfs(struct mem_ctl_info *mci)
+{
+	struct mc_priv *priv = mci->pvt_info;
+
+	priv->debugfs = edac_debugfs_create_dir(DRV_NAME);
+	if (!priv->debugfs) {
+		dev_dbg(mci->pdev, "failed to create debugfs dir\n");
+		return;
+	}
+
+	edac_debugfs_create_x16("bits", 0644, priv->debugfs, &priv->xor_check_bits);
+	edac_debugfs_create_file("inject", 0200, priv->debugfs, &mci->dev, &cdns_ecc_error_fops);
+}
+
+static void cdns_remove_debugfs(struct mem_ctl_info *mci)
+{
+	struct mc_priv *priv = mci->pvt_info;
+
+	edac_debugfs_remove_recursive(priv->debugfs);
+}
+
+#else
+
+static void cdns_setup_debugfs(struct mem_ctl_info *mci) {}
+
+static void cdns_remove_debugfs(struct mem_ctl_info *mci) {}
+
+#endif
+
+static int cdns_setup_irq(struct platform_device *pdev, struct mem_ctl_info *mci)
+{
+	struct mc_priv *priv = mci->pvt_info;
+	int ret;
+
+	priv->irq = platform_get_irq(pdev, 0);
+	if (priv->irq < 0)
+		return dev_err_probe(&pdev->dev, priv->irq, "unable to get irq\n");
+
+	ret = devm_request_irq(&pdev->dev, priv->irq, cdns_mc_err_handler, 0, dev_name(&pdev->dev),
+			       mci);
+	if (ret)
+		return dev_err_probe(&pdev->dev, ret, "unable to request irq %d\n", priv->irq);
+
+	return 0;
+}
+
+static int cdns_setup_mc(struct platform_device *pdev, void __iomem *io_base)
+{
+	struct edac_mc_layer layers[2];
+	struct mem_ctl_info *mci;
+	resource_size_t mem_sz;
+	struct dimm_info *dimm;
+	struct device_node *np;
+	struct mc_priv *priv;
+	struct resource res;
+	int ret;
+	u32 reg;
+
+	layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+	layers[0].size = 1;
+	layers[0].is_virt_csrow = true;
+	layers[1].type = EDAC_MC_LAYER_CHANNEL;
+	layers[1].size = 1;
+	layers[1].is_virt_csrow = false;
+
+	np = of_find_node_by_name(NULL, "memory");
+	if (!np)
+		return dev_err_probe(&pdev->dev, -ENODEV, "unable to find memory node\n");
+
+	ret = of_address_to_resource(np, 0, &res);
+
+	of_node_put(np);
+
+	if (ret)
+		return ret;
+
+	mem_sz = resource_size(&res);
+
+	mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(struct mc_priv));
+	if (!mci)
+		return dev_err_probe(&pdev->dev, -ENOMEM, "unable to allocate edac mc\n");
+
+	mci->pdev = &pdev->dev;
+	priv = mci->pvt_info;
+
+	priv->io_base = io_base;
+	spin_lock_init(&priv->lock);
+	priv->xor_check_bits = 0;
+	platform_set_drvdata(pdev, mci);
+
+	reg = readl(priv->io_base + CDNS_ECC_STAT);
+	if (!CDNS_ECC_STAT_IS_ENABLED(reg))
+		mci->edac_cap = EDAC_FLAG_NONE;
+	else
+		mci->edac_cap = EDAC_FLAG_SECDED;
+
+	mci->mtype_cap = MEM_FLAG_DDR2 | MEM_FLAG_DDR3;
+	mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
+	mci->mod_name = pdev->dev.driver->name;
+	mci->ctl_name = "cdns-ddr-ctrl";
+	mci->dev_name = dev_name(&pdev->dev);
+	mci->scrub_mode = SCRUB_SW_SRC;
+
+	dimm = *mci->dimms;
+	dimm->nr_pages = PFN_UP(mem_sz);
+	dimm->grain = 4;
+	dimm->edac_mode = EDAC_SECDED;
+
+	/* Check if half datapath feature of the controller is active. */
+	reg = readl(priv->io_base + CDNS_BUS_CTRL);
+	if (reg & CDNS_BUS_CTRL_REDUC)
+		dimm->dtype = DEV_X8;
+	else
+		dimm->dtype = DEV_X16;
+
+	strscpy(dimm->label, "Channel#0_DIMM#0", sizeof(dimm->label));
+
+	reg = readl(priv->io_base + CDNS_DDR_STAT);
+	if (CDNS_DDR_STAT_GET_DRAM_CLASS(reg) == CDNS_DDR_STAT_DRAM_DDR2)
+		dimm->mtype = MEM_DDR2;
+	else
+		dimm->mtype = MEM_DDR3;
+
+	ret = edac_mc_add_mc(mci);
+	if (ret) {
+		dev_err_probe(&pdev->dev, ret, "failed to add mc\n");
+		goto free_edac_mc;
+	}
+
+	ret = cdns_setup_irq(pdev, mci);
+	if (ret)
+		goto del_edac_mc;
+
+	cdns_setup_debugfs(mci);
+
+	edac_mc_chipset_printk(mci, KERN_DEBUG, DRV_NAME, "ECC %s\n",
+			       (mci->edac_cap == EDAC_FLAG_NONE) ? "disabled" : "enabled");
+
+	return 0;
+
+del_edac_mc:
+	edac_mc_del_mc(&pdev->dev);
+free_edac_mc:
+	edac_mc_free(mci);
+	return ret;
+}
+
+static int cdns_mc_probe(struct platform_device *pdev)
+{
+	void __iomem *io_base;
+	struct clk *clk;
+	int ret;
+
+	io_base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(io_base))
+		return dev_err_probe(&pdev->dev, PTR_ERR(io_base), "unable to map regs\n");
+
+	clk = devm_clk_get_enabled(&pdev->dev, "ddrc");
+	if (IS_ERR(clk))
+		return dev_err_probe(&pdev->dev, PTR_ERR(clk), "unable to get ddrc\n");
+
+	clk = devm_clk_get_enabled(&pdev->dev, "pclk");
+	if (IS_ERR(clk))
+		return dev_err_probe(&pdev->dev, PTR_ERR(clk), "unable to get pclk\n");
+
+	writel(CDNS_ECC_INT_CTRL_MASK_ALL, io_base + CDNS_ECC_INT_CTRL);
+
+	ret = cdns_setup_mc(pdev, io_base);
+	if (ret)
+		return ret;
+
+	writel(CDNS_ECC_INT_CTRL_UNMASK(CDNS_ECC_INT_EDAC),
+	       io_base + CDNS_ECC_INT_CTRL);
+
+	return 0;
+}
+
+static void cdns_mc_remove(struct platform_device *pdev)
+{
+	struct mem_ctl_info *mci = platform_get_drvdata(pdev);
+	struct mc_priv *priv = mci->pvt_info;
+
+	writel(CDNS_ECC_INT_CTRL_MASK_ALL, priv->io_base + CDNS_ECC_INT_CTRL);
+
+	/* Wait for any pending interrupt before releasing resources. */
+	devm_free_irq(&pdev->dev, priv->irq, mci);
+
+	cdns_remove_debugfs(mci);
+
+	edac_mc_del_mc(&pdev->dev);
+	edac_mc_free(mci);
+}
+
+static const struct of_device_id cdns_ddr_ctrl_of_match[] = { { .compatible = "cdns,ddr-edac" },
+							      {} };
+MODULE_DEVICE_TABLE(of, cdns_ddr_ctrl_of_match);
+
+static struct platform_driver cdns_mc_edac_driver = {
+	.probe = cdns_mc_probe,
+	.remove = cdns_mc_remove,
+	.driver = {
+		.name = DRV_NAME,
+		.of_match_table = cdns_ddr_ctrl_of_match,
+	},
+};
+
+module_platform_driver(cdns_mc_edac_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Renesas Electronics Europe Ltd.");
+MODULE_AUTHOR("Paul Louvel <paul.louvel@bootlin.com>");
+MODULE_DESCRIPTION("EDAC driver for Cadence DDR controller");

-- 
2.56.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH v6 3/3] ARM: dts: renesas: r9a06g032: add EDAC node
  2026-10-13 10:44 [PATCH v6 0/3] Add Renesas RZ/N1x EDAC driver Paul Louvel
  2026-10-13 10:44 ` [PATCH v6 1/3] dt-bindings: edac: cdns,ddr-edac: add Cadence DDR EDAC Paul Louvel
  2026-10-13 10:44 ` [PATCH v6 2/3] EDAC/cadence: Add Cadence DDR EDAC driver Paul Louvel
@ 2026-10-13 10:45 ` Paul Louvel
  2026-10-13 11:41   ` Geert Uytterhoeven
  2 siblings, 1 reply; 8+ messages in thread
From: Paul Louvel @ 2026-10-13 10:45 UTC (permalink / raw)
  To: Borislav Petkov, Tony Luck, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Geert Uytterhoeven, Magnus Damm
  Cc: linux-kernel, linux-renesas-soc, linux-edac, devicetree,
	Thomas Petazzoni, Miquel Raynal, Herve Codina,
	Paul Louvel (Schneider Electric), Wolfram Sang

Add EDAC node to the SoC base device tree file.

Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Paul Louvel (Schneider Electric) <paul.louvel@bootlin.com>
---
 arch/arm/boot/dts/renesas/r9a06g032.dtsi | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/arch/arm/boot/dts/renesas/r9a06g032.dtsi b/arch/arm/boot/dts/renesas/r9a06g032.dtsi
index 19c9bce0a26d..8152464d4e6b 100644
--- a/arch/arm/boot/dts/renesas/r9a06g032.dtsi
+++ b/arch/arm/boot/dts/renesas/r9a06g032.dtsi
@@ -164,6 +164,15 @@ dmamux: dma-router@a0 {
 			};
 		};
 
+		edac: memory-controller@4000d000 {
+			compatible = "renesas,r9a06g032-ddr-edac", "cdns,ddr-edac";
+			reg = <0x4000d000 0x1000>;
+			interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&sysctrl R9A06G032_CLK_DDRC>, <&sysctrl R9A06G032_HCLK_DDRC>;
+			clock-names = "ddrc", "pclk";
+			status = "disabled";
+		};
+
 		udc: usb@4001e000 {
 			compatible = "renesas,r9a06g032-usbf", "renesas,rzn1-usbf";
 			reg = <0x4001e000 0x2000>;

-- 
2.56.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH v6 3/3] ARM: dts: renesas: r9a06g032: add EDAC node
  2026-10-13 10:45 ` [PATCH v6 3/3] ARM: dts: renesas: r9a06g032: add EDAC node Paul Louvel
@ 2026-10-13 11:41   ` Geert Uytterhoeven
  2026-10-13 11:57     ` Paul Louvel
  0 siblings, 1 reply; 8+ messages in thread
From: Geert Uytterhoeven @ 2026-10-13 11:41 UTC (permalink / raw)
  To: Paul Louvel
  Cc: Borislav Petkov, Tony Luck, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Geert Uytterhoeven, Magnus Damm, linux-kernel,
	linux-renesas-soc, linux-edac, devicetree, Thomas Petazzoni,
	Miquel Raynal, Herve Codina, Wolfram Sang

Hi Paul,

On Tue, 13 Oct 2026 at 12:45, Paul Louvel <paul.louvel@bootlin.com> wrote:
> Add EDAC node to the SoC base device tree file.
>
> Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
> Signed-off-by: Paul Louvel (Schneider Electric) <paul.louvel@bootlin.com>

Thanks for your patch!

> --- a/arch/arm/boot/dts/renesas/r9a06g032.dtsi
> +++ b/arch/arm/boot/dts/renesas/r9a06g032.dtsi
> @@ -164,6 +164,15 @@ dmamux: dma-router@a0 {
>                         };
>                 };
>
> +               edac: memory-controller@4000d000 {
> +                       compatible = "renesas,r9a06g032-ddr-edac", "cdns,ddr-edac";
> +                       reg = <0x4000d000 0x1000>;
> +                       interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
> +                       clocks = <&sysctrl R9A06G032_CLK_DDRC>, <&sysctrl R9A06G032_HCLK_DDRC>;
> +                       clock-names = "ddrc", "pclk";
> +                       status = "disabled";

Should this node be enabled by default, or am I too naive?

> +               };
> +
>                 udc: usb@4001e000 {
>                         compatible = "renesas,r9a06g032-usbf", "renesas,rzn1-usbf";
>                         reg = <0x4001e000 0x2000>;
>

Gr{oetje,eeting}s,

                        Geert


--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v6 3/3] ARM: dts: renesas: r9a06g032: add EDAC node
  2026-10-13 11:41   ` Geert Uytterhoeven
@ 2026-10-13 11:57     ` Paul Louvel
  2026-10-13 13:52       ` Miquel Raynal
  0 siblings, 1 reply; 8+ messages in thread
From: Paul Louvel @ 2026-10-13 11:57 UTC (permalink / raw)
  To: Geert Uytterhoeven, Paul Louvel
  Cc: Borislav Petkov, Tony Luck, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Geert Uytterhoeven, Magnus Damm, linux-kernel,
	linux-renesas-soc, linux-edac, devicetree, Thomas Petazzoni,
	Miquel Raynal, Herve Codina, Wolfram Sang

Hi Geert,

On Tue Oct 13, 2026 at 1:41 PM CEST, Geert Uytterhoeven wrote:
> Hi Paul,
>
> On Tue, 13 Oct 2026 at 12:45, Paul Louvel <paul.louvel@bootlin.com> wrote:
>> Add EDAC node to the SoC base device tree file.
>>
>> Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
>> Signed-off-by: Paul Louvel (Schneider Electric) <paul.louvel@bootlin.com>
>
> Thanks for your patch!
>
>> --- a/arch/arm/boot/dts/renesas/r9a06g032.dtsi
>> +++ b/arch/arm/boot/dts/renesas/r9a06g032.dtsi
>> @@ -164,6 +164,15 @@ dmamux: dma-router@a0 {
>>                         };
>>                 };
>>
>> +               edac: memory-controller@4000d000 {
>> +                       compatible = "renesas,r9a06g032-ddr-edac", "cdns,ddr-edac";
>> +                       reg = <0x4000d000 0x1000>;
>> +                       interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
>> +                       clocks = <&sysctrl R9A06G032_CLK_DDRC>, <&sysctrl R9A06G032_HCLK_DDRC>;
>> +                       clock-names = "ddrc", "pclk";
>> +                       status = "disabled";
>
> Should this node be enabled by default, or am I too naive?

The bootloader will be responsible to initialize the DDR controller and enable
ECC if specified. On this specific platform (r9a06g032), ECC is disabled by
default.
Enabling the ECC require a specific property to be present under the controller
node in U-Boot. I think it make sense to keep it by default disabled.

>
>> +               };
>> +
>>                 udc: usb@4001e000 {
>>                         compatible = "renesas,r9a06g032-usbf", "renesas,rzn1-usbf";
>>                         reg = <0x4001e000 0x2000>;
>>
>
> Gr{oetje,eeting}s,
>
>                         Geert
>
>
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
>
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
>                                 -- Linus Torvalds





-- 
Paul Louvel, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v6 3/3] ARM: dts: renesas: r9a06g032: add EDAC node
  2026-10-13 11:57     ` Paul Louvel
@ 2026-10-13 13:52       ` Miquel Raynal
  2026-10-13 14:14         ` Paul Louvel
  0 siblings, 1 reply; 8+ messages in thread
From: Miquel Raynal @ 2026-10-13 13:52 UTC (permalink / raw)
  To: Paul Louvel
  Cc: Geert Uytterhoeven, Borislav Petkov, Tony Luck, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Geert Uytterhoeven,
	Magnus Damm, linux-kernel, linux-renesas-soc, linux-edac,
	devicetree, Thomas Petazzoni, Herve Codina, Wolfram Sang


>>> +               edac: memory-controller@4000d000 {
>>> +                       compatible = "renesas,r9a06g032-ddr-edac", "cdns,ddr-edac";
>>> +                       reg = <0x4000d000 0x1000>;
>>> +                       interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
>>> +                       clocks = <&sysctrl R9A06G032_CLK_DDRC>, <&sysctrl R9A06G032_HCLK_DDRC>;
>>> +                       clock-names = "ddrc", "pclk";
>>> +                       status = "disabled";
>>
>> Should this node be enabled by default, or am I too naive?
>
> The bootloader will be responsible to initialize the DDR controller and enable
> ECC if specified. On this specific platform (r9a06g032), ECC is disabled by
> default.
> Enabling the ECC require a specific property to be present under the controller
> node in U-Boot. I think it make sense to keep it by default disabled.

Wouldn't enabling it also require some reserved memory regions? There
should be an impact on the amount of available RAM for the system.

Thanks,
Miquèl

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v6 3/3] ARM: dts: renesas: r9a06g032: add EDAC node
  2026-10-13 13:52       ` Miquel Raynal
@ 2026-10-13 14:14         ` Paul Louvel
  0 siblings, 0 replies; 8+ messages in thread
From: Paul Louvel @ 2026-10-13 14:14 UTC (permalink / raw)
  To: Miquel Raynal, Paul Louvel
  Cc: Geert Uytterhoeven, Borislav Petkov, Tony Luck, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Geert Uytterhoeven,
	Magnus Damm, linux-kernel, linux-renesas-soc, linux-edac,
	devicetree, Thomas Petazzoni, Herve Codina, Wolfram Sang

On Tue Oct 13, 2026 at 3:52 PM CEST, Miquel Raynal wrote:
>
>>>> +               edac: memory-controller@4000d000 {
>>>> +                       compatible = "renesas,r9a06g032-ddr-edac", "cdns,ddr-edac";
>>>> +                       reg = <0x4000d000 0x1000>;
>>>> +                       interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
>>>> +                       clocks = <&sysctrl R9A06G032_CLK_DDRC>, <&sysctrl R9A06G032_HCLK_DDRC>;
>>>> +                       clock-names = "ddrc", "pclk";
>>>> +                       status = "disabled";
>>>
>>> Should this node be enabled by default, or am I too naive?
>>
>> The bootloader will be responsible to initialize the DDR controller and enable
>> ECC if specified. On this specific platform (r9a06g032), ECC is disabled by
>> default.
>> Enabling the ECC require a specific property to be present under the controller
>> node in U-Boot. I think it make sense to keep it by default disabled.
>
> Wouldn't enabling it also require some reserved memory regions? There
> should be an impact on the amount of available RAM for the system.

Yes, if the memory controller is configured by the bootloader to enable ECC.
U-Boot would fixup the memory node to reflect the correct amount of available
RAM in this case.

At the Linux level, it would not change anything if the EDAC driver is probed by
and ECC is disabled. It would just simply inform the EDAC core that SECDEC is
not supported and that's it.

>
> Thanks,
> Miquèl

-- 
Paul Louvel, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-10-13 14:14 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-13 10:44 [PATCH v6 0/3] Add Renesas RZ/N1x EDAC driver Paul Louvel
2026-10-13 10:44 ` [PATCH v6 1/3] dt-bindings: edac: cdns,ddr-edac: add Cadence DDR EDAC Paul Louvel
2026-10-13 10:44 ` [PATCH v6 2/3] EDAC/cadence: Add Cadence DDR EDAC driver Paul Louvel
2026-10-13 10:45 ` [PATCH v6 3/3] ARM: dts: renesas: r9a06g032: add EDAC node Paul Louvel
2026-10-13 11:41   ` Geert Uytterhoeven
2026-10-13 11:57     ` Paul Louvel
2026-10-13 13:52       ` Miquel Raynal
2026-10-13 14:14         ` Paul Louvel

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox