linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v13 0/3] EDAC/nuvoton: Add NPCM memory controller driver
@ 2022-08-16  9:46 Marvin Lin
  2022-08-16  9:46 ` [PATCH v13 1/3] arm: dts: nuvoton: Add node for NPCM memory controller Marvin Lin
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Marvin Lin @ 2022-08-16  9:46 UTC (permalink / raw)
  To: linux-edac, rric, james.morse, tony.luck, mchehab, bp, robh+dt,
	linux-kernel
  Cc: devicetree, openbmc, benjaminfair, yuenn, venture, KWLIU, YSCHU,
	JJLIU0, KFTING, avifishman70, tmaimon77, tali.perry1, ctcchien,
	kflin, Marvin Lin

This patch series add DTS node, dt-bindings document and driver for memory
controller present on Nuvoton NPCM SoCs.

The memory controller supports single bit error correction and double bit
error detection (in-line ECC in which a section 1/8th of the memory device
used to store data is used for ECC storage).

Marvin Lin (3):
  arm: dts: nuvoton: Add node for NPCM memory controller
  dt-bindings: edac: nuvoton: Add document for NPCM memory controller
  EDAC/nuvoton: Add NPCM memory controller driver

 .../edac/nuvoton,npcm-memory-controller.yaml  |  54 ++
 MAINTAINERS                                   |   7 +
 arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi |   7 +
 drivers/edac/Kconfig                          |  11 +
 drivers/edac/Makefile                         |   1 +
 drivers/edac/npcm_edac.c                      | 512 ++++++++++++++++++
 6 files changed, 592 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/edac/nuvoton,npcm-memory-controller.yaml
 create mode 100644 drivers/edac/npcm_edac.c

-- 
2.17.1


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

* [PATCH v13 1/3] arm: dts: nuvoton: Add node for NPCM memory controller
  2022-08-16  9:46 [PATCH v13 0/3] EDAC/nuvoton: Add NPCM memory controller driver Marvin Lin
@ 2022-08-16  9:46 ` Marvin Lin
  2022-08-16  9:46 ` [PATCH v13 2/3] dt-bindings: edac: nuvoton: Add document " Marvin Lin
  2022-08-16  9:46 ` [PATCH v13 3/3] EDAC/nuvoton: Add NPCM memory controller driver Marvin Lin
  2 siblings, 0 replies; 7+ messages in thread
From: Marvin Lin @ 2022-08-16  9:46 UTC (permalink / raw)
  To: linux-edac, rric, james.morse, tony.luck, mchehab, bp, robh+dt,
	linux-kernel
  Cc: devicetree, openbmc, benjaminfair, yuenn, venture, KWLIU, YSCHU,
	JJLIU0, KFTING, avifishman70, tmaimon77, tali.perry1, ctcchien,
	kflin, Marvin Lin

Add node for memory controller present on Nuvoton NPCM SoCs. The memory
controller supports single bit error correction and double bit error
detection.

Signed-off-by: Marvin Lin <milkfafa@gmail.com>
---
 arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi b/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi
index c7b5ef15b716..d875e8ac1e09 100644
--- a/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi
+++ b/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi
@@ -179,6 +179,13 @@
 			status = "disabled";
 		};
 
+		mc: memory-controller@f0824000 {
+			compatible = "nuvoton,npcm750-memory-controller";
+			reg = <0xf0824000 0x1000>;
+			interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+		};
+
 		apb {
 			#address-cells = <1>;
 			#size-cells = <1>;
-- 
2.17.1


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

* [PATCH v13 2/3] dt-bindings: edac: nuvoton: Add document for NPCM memory controller
  2022-08-16  9:46 [PATCH v13 0/3] EDAC/nuvoton: Add NPCM memory controller driver Marvin Lin
  2022-08-16  9:46 ` [PATCH v13 1/3] arm: dts: nuvoton: Add node for NPCM memory controller Marvin Lin
@ 2022-08-16  9:46 ` Marvin Lin
  2022-08-18 14:44   ` Rob Herring
  2022-08-16  9:46 ` [PATCH v13 3/3] EDAC/nuvoton: Add NPCM memory controller driver Marvin Lin
  2 siblings, 1 reply; 7+ messages in thread
From: Marvin Lin @ 2022-08-16  9:46 UTC (permalink / raw)
  To: linux-edac, rric, james.morse, tony.luck, mchehab, bp, robh+dt,
	linux-kernel
  Cc: devicetree, openbmc, benjaminfair, yuenn, venture, KWLIU, YSCHU,
	JJLIU0, KFTING, avifishman70, tmaimon77, tali.perry1, ctcchien,
	kflin, Marvin Lin

Add dt-bindings document for Nuvoton NPCM memory controller.

Signed-off-by: Marvin Lin <milkfafa@gmail.com>
---
 .../edac/nuvoton,npcm-memory-controller.yaml  | 54 +++++++++++++++++++
 1 file changed, 54 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/edac/nuvoton,npcm-memory-controller.yaml

diff --git a/Documentation/devicetree/bindings/edac/nuvoton,npcm-memory-controller.yaml b/Documentation/devicetree/bindings/edac/nuvoton,npcm-memory-controller.yaml
new file mode 100644
index 000000000000..d5ef7e7a65f9
--- /dev/null
+++ b/Documentation/devicetree/bindings/edac/nuvoton,npcm-memory-controller.yaml
@@ -0,0 +1,54 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/edac/nuvoton,npcm-memory-controller.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nuvoton NPCM Memory Controller Device Tree Bindings
+
+maintainers:
+  - Marvin Lin <kflin@nuvoton.com>
+  - Stanley Chu <yschu@nuvoton.com>
+
+description: |
+  The Nuvoton BMC SoC supports DDR4 memory with and without ECC (error
+  correction check).
+
+  The memory controller supports single bit error correction, double bit
+  error detection (in-line ECC in which a section (1/8th) of the memory
+  device used to store data is used for ECC storage).
+
+  Note, the bootloader must configure ECC mode for the memory controller.
+
+properties:
+  compatible:
+    enum:
+      - nuvoton,npcm750-memory-controller
+      - nuvoton,npcm845-memory-controller
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - interrupts
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    ahb {
+        #address-cells = <1>;
+        #size-cells = <1>;
+        mc: memory-controller@f0824000 {
+            compatible = "nuvoton,npcm750-memory-controller";
+            reg = <0xf0824000 0x1000>;
+            interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
+        };
+    };
-- 
2.17.1


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

* [PATCH v13 3/3] EDAC/nuvoton: Add NPCM memory controller driver
  2022-08-16  9:46 [PATCH v13 0/3] EDAC/nuvoton: Add NPCM memory controller driver Marvin Lin
  2022-08-16  9:46 ` [PATCH v13 1/3] arm: dts: nuvoton: Add node for NPCM memory controller Marvin Lin
  2022-08-16  9:46 ` [PATCH v13 2/3] dt-bindings: edac: nuvoton: Add document " Marvin Lin
@ 2022-08-16  9:46 ` Marvin Lin
  2022-08-26 15:59   ` kernel test robot
  2 siblings, 1 reply; 7+ messages in thread
From: Marvin Lin @ 2022-08-16  9:46 UTC (permalink / raw)
  To: linux-edac, rric, james.morse, tony.luck, mchehab, bp, robh+dt,
	linux-kernel
  Cc: devicetree, openbmc, benjaminfair, yuenn, venture, KWLIU, YSCHU,
	JJLIU0, KFTING, avifishman70, tmaimon77, tali.perry1, ctcchien,
	kflin, Marvin Lin

Add driver for memory controller present on Nuvoton NPCM SoCs. The memory
controller supports single bit error correction and double bit error
detection.

Signed-off-by: Marvin Lin <milkfafa@gmail.com>
---
 MAINTAINERS              |   7 +
 drivers/edac/Kconfig     |  11 +
 drivers/edac/Makefile    |   1 +
 drivers/edac/npcm_edac.c | 512 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 531 insertions(+)
 create mode 100644 drivers/edac/npcm_edac.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 868bbf31603d..dde5b3e647ce 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7409,6 +7409,13 @@ L:	linux-edac@vger.kernel.org
 S:	Maintained
 F:	drivers/edac/mpc85xx_edac.[ch]
 
+EDAC-NPCM
+M:	Marvin Lin <kflin@nuvoton.com>
+L:	linux-edac@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/edac/nuvoton,npcm-memory-controller.yaml
+F:	drivers/edac/npcm_edac.c
+
 EDAC-PASEMI
 M:	Egor Martovetsky <egor@pasemi.com>
 L:	linux-edac@vger.kernel.org
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index 17562cf1fe97..69b92dff7dbb 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -541,4 +541,15 @@ config EDAC_DMC520
 	  Support for error detection and correction on the
 	  SoCs with ARM DMC-520 DRAM controller.
 
+config EDAC_NPCM
+	tristate "Nuvoton NPCM DDR Memory Controller"
+	depends on (ARCH_NPCM || COMPILE_TEST)
+	help
+	  Support for error detection and correction on the Nuvoton NPCM DDR
+	  memory controller.
+
+	  The memory controller supports single bit error correction, double bit
+	  error detection (in-line ECC in which a section 1/8th of the memory
+	  device used to store data is used for ECC storage).
+
 endif # EDAC
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index 2d1641a27a28..db3c59d3ad84 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -84,3 +84,4 @@ obj-$(CONFIG_EDAC_QCOM)			+= qcom_edac.o
 obj-$(CONFIG_EDAC_ASPEED)		+= aspeed_edac.o
 obj-$(CONFIG_EDAC_BLUEFIELD)		+= bluefield_edac.o
 obj-$(CONFIG_EDAC_DMC520)		+= dmc520_edac.o
+obj-$(CONFIG_EDAC_NPCM)			+= npcm_edac.o
diff --git a/drivers/edac/npcm_edac.c b/drivers/edac/npcm_edac.c
new file mode 100644
index 000000000000..067b5d7b28fe
--- /dev/null
+++ b/drivers/edac/npcm_edac.c
@@ -0,0 +1,512 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2022 Nuvoton Technology Corporation
+
+#include <linux/debugfs.h>
+#include <linux/iopoll.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include "edac_module.h"
+
+#define EDAC_MOD_NAME	"npcm-edac"
+#define EDAC_MSG_SIZE	256
+
+/* chip serials */
+#define NPCM7XX_CHIP	BIT(0)
+#define NPCM8XX_CHIP	BIT(1)
+
+/* syndrome values */
+#define UE_SYNDROME	0x03
+
+static char data_synd[] = {
+	0xf4, 0xf1, 0xec, 0xea, 0xe9, 0xe6, 0xe5, 0xe3,
+	0xdc, 0xda, 0xd9, 0xd6, 0xd5, 0xd3, 0xce, 0xcb,
+	0xb5, 0xb0, 0xad, 0xab, 0xa8, 0xa7, 0xa4, 0xa2,
+	0x9d, 0x9b, 0x98, 0x97, 0x94, 0x92, 0x8f, 0x8a,
+	0x75, 0x70, 0x6d, 0x6b, 0x68, 0x67, 0x64, 0x62,
+	0x5e, 0x5b, 0x58, 0x57, 0x54, 0x52, 0x4f, 0x4a,
+	0x34, 0x31, 0x2c, 0x2a, 0x29, 0x26, 0x25, 0x23,
+	0x1c, 0x1a, 0x19, 0x16, 0x15, 0x13, 0x0e, 0x0b
+};
+
+static struct regmap *npcm_regmap;
+
+struct npcm_platform_data {
+	/* chip serials */
+	int chip;
+
+	/* memory controller registers */
+	u32 ctl_ecc_en;
+	u32 ctl_int_status;
+	u32 ctl_int_ack;
+	u32 ctl_int_mask_master;
+	u32 ctl_int_mask_ecc;
+	u32 ctl_ce_addr_l;
+	u32 ctl_ce_addr_h;
+	u32 ctl_ce_data_l;
+	u32 ctl_ce_data_h;
+	u32 ctl_ce_synd;
+	u32 ctl_ue_addr_l;
+	u32 ctl_ue_addr_h;
+	u32 ctl_ue_data_l;
+	u32 ctl_ue_data_h;
+	u32 ctl_ue_synd;
+	u32 ctl_source_id;
+	u32 ctl_controller_busy;
+	u32 ctl_xor_check_bits;
+
+	/* masks and shifts */
+	u32 ecc_en_mask;
+	u32 int_status_ce_mask;
+	u32 int_status_ue_mask;
+	u32 int_ack_ce_mask;
+	u32 int_ack_ue_mask;
+	u32 int_mask_master_non_ecc_mask;
+	u32 int_mask_master_global_mask;
+	u32 int_mask_ecc_non_event_mask;
+	u32 ce_addr_h_mask;
+	u32 ce_synd_mask;
+	u32 ce_synd_shift;
+	u32 ue_addr_h_mask;
+	u32 ue_synd_mask;
+	u32 ue_synd_shift;
+	u32 source_id_ce_mask;
+	u32 source_id_ce_shift;
+	u32 source_id_ue_mask;
+	u32 source_id_ue_shift;
+	u32 controller_busy_mask;
+	u32 xor_check_bits_mask;
+	u32 xor_check_bits_shift;
+	u32 writeback_en_mask;
+	u32 fwc_mask;
+};
+
+struct priv_data {
+	void __iomem *reg;
+	char message[EDAC_MSG_SIZE];
+	const struct npcm_platform_data *pdata;
+
+	/* error injection */
+	struct dentry *debugfs;
+	u8 error_type;
+	u8 location;
+	u8 bit;
+};
+
+static void handle_ce(struct mem_ctl_info *mci)
+{
+	struct priv_data *priv = mci->pvt_info;
+	const struct npcm_platform_data *pdata = priv->pdata;
+	u64 addr, data;
+	u32 val_l, val_h, id, synd;
+
+	regmap_read(npcm_regmap, pdata->ctl_ce_addr_l, &val_l);
+	if (pdata->chip == NPCM8XX_CHIP) {
+		regmap_read(npcm_regmap, pdata->ctl_ce_addr_h, &val_h);
+		val_h &= pdata->ce_addr_h_mask;
+	}
+	addr = ((addr | val_h) << 32) | val_l;
+
+	regmap_read(npcm_regmap, pdata->ctl_ce_data_l, &val_l);
+	if (pdata->chip == NPCM8XX_CHIP)
+		regmap_read(npcm_regmap, pdata->ctl_ce_data_h, &val_h);
+	data = ((data | val_h) << 32) | val_l;
+
+	regmap_read(npcm_regmap, pdata->ctl_source_id, &id);
+	id = (id & pdata->source_id_ce_mask) >> pdata->source_id_ce_shift;
+
+	regmap_read(npcm_regmap, pdata->ctl_ce_synd, &synd);
+	synd = (synd & pdata->ce_synd_mask) >> pdata->ce_synd_shift;
+
+	snprintf(priv->message, EDAC_MSG_SIZE,
+		 "addr = 0x%llx, data = 0x%llx, id = 0x%x", addr, data, id);
+
+	edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, addr >> PAGE_SHIFT,
+			     addr & ~PAGE_MASK, synd, 0, 0, -1, priv->message, "");
+}
+
+static void handle_ue(struct mem_ctl_info *mci)
+{
+	struct priv_data *priv = mci->pvt_info;
+	const struct npcm_platform_data *pdata = priv->pdata;
+	u64 addr, data;
+	u32 val_l, val_h, id, synd;
+
+	regmap_read(npcm_regmap, pdata->ctl_ue_addr_l, &val_l);
+	if (pdata->chip == NPCM8XX_CHIP) {
+		regmap_read(npcm_regmap, pdata->ctl_ue_addr_h, &val_h);
+		val_h &= pdata->ue_addr_h_mask;
+	}
+	addr = ((addr | val_h) << 32) | val_l;
+
+	regmap_read(npcm_regmap, pdata->ctl_ue_data_l, &val_l);
+	if (pdata->chip == NPCM8XX_CHIP)
+		regmap_read(npcm_regmap, pdata->ctl_ue_data_h, &val_h);
+	data = ((data | val_h) << 32) | val_l;
+
+	regmap_read(npcm_regmap, pdata->ctl_source_id, &id);
+	id = (id & pdata->source_id_ue_mask) >> pdata->source_id_ue_shift;
+
+	regmap_read(npcm_regmap, pdata->ctl_ue_synd, &synd);
+	synd = (synd & pdata->ue_synd_mask) >> pdata->ue_synd_shift;
+
+	snprintf(priv->message, EDAC_MSG_SIZE,
+		 "addr = 0x%llx, data = 0x%llx, id = 0x%x", addr, data, id);
+
+	edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, addr >> PAGE_SHIFT,
+			     addr & ~PAGE_MASK, synd, 0, 0, -1, priv->message, "");
+}
+
+static irqreturn_t edac_ecc_isr(int irq, void *dev_id)
+{
+	struct mem_ctl_info *mci = dev_id;
+	struct priv_data *priv = mci->pvt_info;
+	const struct npcm_platform_data *pdata = priv->pdata;
+	u32 status;
+
+	regmap_read(npcm_regmap, pdata->ctl_int_status, &status);
+	if (status & pdata->int_status_ce_mask) {
+		handle_ce(mci);
+
+		/* acknowledge the CE interrupt */
+		regmap_write(npcm_regmap, pdata->ctl_int_ack,
+			     pdata->int_ack_ce_mask);
+		return IRQ_HANDLED;
+	} else if (status & pdata->int_status_ue_mask) {
+		handle_ue(mci);
+
+		/* acknowledge the UE interrupt */
+		regmap_write(npcm_regmap, pdata->ctl_int_ack,
+			     pdata->int_ack_ue_mask);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+static ssize_t 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 priv_data *priv = mci->pvt_info;
+	struct npcm_platform_data *pdata = priv->pdata;
+	int ret;
+	u32 val, syndrome;
+
+	/*
+	 * error_type - 0: CE, 1: UE
+	 * location   - 0: data, 1: checkcode
+	 * bit        - 0 ~ 63 for data and 0 ~ 7 for checkcode
+	 */
+	edac_printk(KERN_INFO, EDAC_MOD_NAME,
+		    "force an ECC error, type = %d, location = %d, bit = %d\n",
+		    priv->error_type, priv->location, priv->bit);
+
+	/* ensure no pending writes */
+	ret = regmap_read_poll_timeout(npcm_regmap, pdata->ctl_controller_busy,
+				       val, !(val & pdata->controller_busy_mask),
+				       1000, 10000);
+	if (ret) {
+		edac_printk(KERN_INFO, EDAC_MOD_NAME,
+			    "wait pending writes timeout\n");
+		return count;
+	}
+
+	regmap_read(npcm_regmap, pdata->ctl_xor_check_bits, &val);
+	val &= ~pdata->xor_check_bits_mask;
+
+	/* write syndrome to XOR_CHECK_BITS */
+	if (priv->error_type == 0) {
+		if (priv->location == 0 && priv->bit > 63) {
+			edac_printk(KERN_INFO, EDAC_MOD_NAME,
+				    "data bit should not exceed 63\n");
+			return count;
+		}
+
+		if (priv->location == 1 && priv->bit > 7) {
+			edac_printk(KERN_INFO, EDAC_MOD_NAME,
+				    "checkcode bit should not exceed 7\n");
+			return count;
+		}
+
+		syndrome = priv->location ? 1 << priv->bit :
+			   data_synd[priv->bit];
+
+		regmap_write(npcm_regmap, pdata->ctl_xor_check_bits,
+			     val | (syndrome << pdata->xor_check_bits_shift) |
+			     pdata->writeback_en_mask);
+	} else if (priv->error_type == 1) {
+		regmap_write(npcm_regmap, pdata->ctl_xor_check_bits,
+			     val | (UE_SYNDROME << pdata->xor_check_bits_shift));
+	}
+
+	/* force write check */
+	regmap_update_bits(npcm_regmap, pdata->ctl_xor_check_bits,
+			   pdata->fwc_mask, pdata->fwc_mask);
+
+	return count;
+}
+
+static const struct file_operations force_ecc_error_fops = {
+	.open = simple_open,
+	.write = force_ecc_error,
+	.llseek = generic_file_llseek,
+};
+
+static void setup_debugfs(struct mem_ctl_info *mci)
+{
+	struct priv_data *priv = mci->pvt_info;
+
+	priv->debugfs = edac_debugfs_create_dir(mci->mod_name);
+	if (!priv->debugfs)
+		return;
+
+	edac_debugfs_create_x8("error_type", 0644, priv->debugfs, &priv->error_type);
+	edac_debugfs_create_x8("location", 0644, priv->debugfs, &priv->location);
+	edac_debugfs_create_x8("bit", 0644, priv->debugfs, &priv->bit);
+	edac_debugfs_create_file("force_ecc_error", 0200, priv->debugfs,
+				 &mci->dev, &force_ecc_error_fops);
+}
+
+static int setup_irq(struct mem_ctl_info *mci, struct platform_device *pdev)
+{
+	struct priv_data *priv = mci->pvt_info;
+	const struct npcm_platform_data *pdata = priv->pdata;
+	int ret, irq;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		edac_printk(KERN_ERR, EDAC_MOD_NAME, "IRQ not defined in DTS\n");
+		return irq;
+	}
+
+	ret = devm_request_irq(&pdev->dev, irq, edac_ecc_isr, 0,
+			       dev_name(&pdev->dev), mci);
+	if (ret < 0) {
+		edac_printk(KERN_ERR, EDAC_MOD_NAME, "failed to request IRQ\n");
+		return ret;
+	}
+
+	/* enable the functional group of ECC and mask the others */
+	regmap_write(npcm_regmap, pdata->ctl_int_mask_master,
+		     pdata->int_mask_master_non_ecc_mask);
+
+	if (pdata->chip == NPCM8XX_CHIP)
+		regmap_write(npcm_regmap, pdata->ctl_int_mask_ecc,
+			     pdata->int_mask_ecc_non_event_mask);
+
+	return 0;
+}
+
+static const struct regmap_config npcm_regmap_cfg = {
+	.reg_bits	= 32,
+	.reg_stride	= 4,
+	.val_bits	= 32,
+};
+
+static int edac_probe(struct platform_device *pdev)
+{
+	const struct npcm_platform_data *pdata;
+	struct device *dev = &pdev->dev;
+	struct edac_mc_layer layers[1];
+	struct mem_ctl_info *mci;
+	struct priv_data *priv;
+	void __iomem *reg;
+	int rc;
+	u32 val;
+
+	reg = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(reg))
+		return PTR_ERR(reg);
+
+	npcm_regmap = devm_regmap_init_mmio(dev, reg, &npcm_regmap_cfg);
+	if (IS_ERR(npcm_regmap))
+		return PTR_ERR(npcm_regmap);
+
+	pdata = of_device_get_match_data(dev);
+	if (!pdata)
+		return -EINVAL;
+
+	/* bail out if ECC is not enabled */
+	regmap_read(npcm_regmap, pdata->ctl_ecc_en, &val);
+	if (!(val & pdata->ecc_en_mask)) {
+		edac_printk(KERN_ERR, EDAC_MOD_NAME, "ECC is not enabled\n");
+		return -EPERM;
+	}
+
+	edac_op_state = EDAC_OPSTATE_INT;
+
+	layers[0].type = EDAC_MC_LAYER_ALL_MEM;
+	layers[0].size = 1;
+
+	mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
+			    sizeof(struct priv_data));
+	if (!mci)
+		return -ENOMEM;
+
+	mci->pdev = &pdev->dev;
+	priv = mci->pvt_info;
+	priv->reg = reg;
+	priv->pdata = pdata;
+	platform_set_drvdata(pdev, mci);
+
+	mci->mtype_cap = MEM_FLAG_DDR4;
+	mci->edac_ctl_cap = EDAC_FLAG_SECDED;
+	mci->scrub_cap = SCRUB_FLAG_HW_SRC;
+	mci->scrub_mode = SCRUB_HW_SRC;
+	mci->edac_cap = EDAC_FLAG_SECDED;
+	mci->ctl_name = "npcm_ddr_controller";
+	mci->dev_name = dev_name(&pdev->dev);
+	mci->mod_name = EDAC_MOD_NAME;
+	mci->ctl_page_to_phys = NULL;
+
+	rc = setup_irq(mci, pdev);
+	if (rc)
+		goto free_edac_mc;
+
+	rc = edac_mc_add_mc(mci);
+	if (rc)
+		goto free_edac_mc;
+
+	if (IS_ENABLED(CONFIG_EDAC_DEBUG) && pdata->chip == NPCM8XX_CHIP)
+		setup_debugfs(mci);
+
+	return rc;
+
+free_edac_mc:
+	edac_mc_free(mci);
+	return rc;
+}
+
+static int edac_remove(struct platform_device *pdev)
+{
+	struct mem_ctl_info *mci = platform_get_drvdata(pdev);
+	struct priv_data *priv = mci->pvt_info;
+	const struct npcm_platform_data *pdata = priv->pdata;
+
+	regmap_write(npcm_regmap, pdata->ctl_int_mask_master,
+		     pdata->int_mask_master_global_mask);
+	regmap_update_bits(npcm_regmap, pdata->ctl_ecc_en, pdata->ecc_en_mask,
+			   0);
+
+	edac_mc_del_mc(&pdev->dev);
+	edac_mc_free(mci);
+
+	if (IS_ENABLED(CONFIG_EDAC_DEBUG) && pdata->chip == NPCM8XX_CHIP)
+		edac_debugfs_remove_recursive(priv->debugfs);
+
+	return 0;
+}
+
+static const struct npcm_platform_data npcm750_edac = {
+	.chip				= NPCM7XX_CHIP,
+
+	/* memory controller registers */
+	.ctl_ecc_en			= 0x174,
+	.ctl_int_status			= 0x1d0,
+	.ctl_int_ack			= 0x1d4,
+	.ctl_int_mask_master		= 0x1d8,
+	.ctl_ce_addr_l			= 0x188,
+	.ctl_ce_data_l			= 0x190,
+	.ctl_ce_synd			= 0x18c,
+	.ctl_ue_addr_l			= 0x17c,
+	.ctl_ue_data_l			= 0x184,
+	.ctl_ue_synd			= 0x180,
+	.ctl_source_id			= 0x194,
+
+	/* masks and shifts */
+	.ecc_en_mask			= BIT(24),
+	.int_status_ce_mask		= GENMASK(4, 3),
+	.int_status_ue_mask		= GENMASK(6, 5),
+	.int_ack_ce_mask		= GENMASK(4, 3),
+	.int_ack_ue_mask		= GENMASK(6, 5),
+	.int_mask_master_non_ecc_mask	= GENMASK(30, 7) | GENMASK(2, 0),
+	.int_mask_master_global_mask	= BIT(31),
+	.ce_synd_mask			= GENMASK(6, 0),
+	.ce_synd_shift			= 0,
+	.ue_synd_mask			= GENMASK(6, 0),
+	.ue_synd_shift			= 0,
+	.source_id_ce_mask		= GENMASK(29, 16),
+	.source_id_ce_shift		= 16,
+	.source_id_ue_mask		= GENMASK(13, 0),
+	.source_id_ue_shift		= 0,
+};
+
+static const struct npcm_platform_data npcm845_edac = {
+	.chip =				NPCM8XX_CHIP,
+
+	/* memory controller registers */
+	.ctl_ecc_en			= 0x16c,
+	.ctl_int_status			= 0x228,
+	.ctl_int_ack			= 0x244,
+	.ctl_int_mask_master		= 0x220,
+	.ctl_int_mask_ecc		= 0x260,
+	.ctl_ce_addr_l			= 0x18c,
+	.ctl_ce_addr_h			= 0x190,
+	.ctl_ce_data_l			= 0x194,
+	.ctl_ce_data_h			= 0x198,
+	.ctl_ce_synd			= 0x190,
+	.ctl_ue_addr_l			= 0x17c,
+	.ctl_ue_addr_h			= 0x180,
+	.ctl_ue_data_l			= 0x184,
+	.ctl_ue_data_h			= 0x188,
+	.ctl_ue_synd			= 0x180,
+	.ctl_source_id			= 0x19c,
+	.ctl_controller_busy		= 0x20c,
+	.ctl_xor_check_bits		= 0x174,
+
+	/* masks and shifts */
+	.ecc_en_mask			= GENMASK(17, 16),
+	.int_status_ce_mask		= GENMASK(1, 0),
+	.int_status_ue_mask		= GENMASK(3, 2),
+	.int_ack_ce_mask		= GENMASK(1, 0),
+	.int_ack_ue_mask		= GENMASK(3, 2),
+	.int_mask_master_non_ecc_mask	= GENMASK(30, 3) | GENMASK(1, 0),
+	.int_mask_master_global_mask	= BIT(31),
+	.int_mask_ecc_non_event_mask	= GENMASK(8, 4),
+	.ce_addr_h_mask			= GENMASK(1, 0),
+	.ce_synd_mask			= GENMASK(15, 8),
+	.ce_synd_shift			= 8,
+	.ue_addr_h_mask			= GENMASK(1, 0),
+	.ue_synd_mask			= GENMASK(15, 8),
+	.ue_synd_shift			= 8,
+	.source_id_ce_mask		= GENMASK(29, 16),
+	.source_id_ce_shift		= 16,
+	.source_id_ue_mask		= GENMASK(13, 0),
+	.source_id_ue_shift		= 0,
+	.controller_busy_mask		= BIT(0),
+	.xor_check_bits_mask		= GENMASK(23, 16),
+	.xor_check_bits_shift		= 16,
+	.writeback_en_mask		= BIT(24),
+	.fwc_mask			= BIT(8),
+};
+
+static const struct of_device_id npcm_edac_of_match[] = {
+	{
+		.compatible = "nuvoton,npcm750-memory-controller",
+		.data = &npcm750_edac
+	},
+	{
+		.compatible = "nuvoton,npcm845-memory-controller",
+		.data = &npcm845_edac
+	},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, npcm_edac_of_match);
+
+static struct platform_driver npcm_edac_driver = {
+	.driver = {
+		.name = "npcm-edac",
+		.of_match_table = npcm_edac_of_match,
+	},
+	.probe = edac_probe,
+	.remove = edac_remove,
+};
+
+module_platform_driver(npcm_edac_driver);
+
+MODULE_AUTHOR("Medad CChien <medadyoung@gmail.com>");
+MODULE_AUTHOR("Marvin Lin <kflin@nuvoton.com>");
+MODULE_DESCRIPTION("Nuvoton NPCM EDAC Driver");
+MODULE_LICENSE("GPL");
-- 
2.17.1


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

* Re: [PATCH v13 2/3] dt-bindings: edac: nuvoton: Add document for NPCM memory controller
  2022-08-16  9:46 ` [PATCH v13 2/3] dt-bindings: edac: nuvoton: Add document " Marvin Lin
@ 2022-08-18 14:44   ` Rob Herring
  2022-08-29  3:52     ` Kun-Fa Lin
  0 siblings, 1 reply; 7+ messages in thread
From: Rob Herring @ 2022-08-18 14:44 UTC (permalink / raw)
  To: Marvin Lin
  Cc: linux-edac, rric, james.morse, tony.luck, mchehab, bp,
	linux-kernel, KWLIU, YSCHU, benjaminfair, devicetree,
	avifishman70, venture, openbmc, KFTING, JJLIU0, tali.perry1,
	ctcchien, kflin, tmaimon77

On Tue, Aug 16, 2022 at 05:46:40PM +0800, Marvin Lin wrote:
> Add dt-bindings document for Nuvoton NPCM memory controller.
> 
> Signed-off-by: Marvin Lin <milkfafa@gmail.com>
> ---
>  .../edac/nuvoton,npcm-memory-controller.yaml  | 54 +++++++++++++++++++
>  1 file changed, 54 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/edac/nuvoton,npcm-memory-controller.yaml

Reviewed-by: Rob Herring <robh@kernel.org>

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

* Re: [PATCH v13 3/3] EDAC/nuvoton: Add NPCM memory controller driver
  2022-08-16  9:46 ` [PATCH v13 3/3] EDAC/nuvoton: Add NPCM memory controller driver Marvin Lin
@ 2022-08-26 15:59   ` kernel test robot
  0 siblings, 0 replies; 7+ messages in thread
From: kernel test robot @ 2022-08-26 15:59 UTC (permalink / raw)
  To: Marvin Lin, linux-edac, rric, james.morse, tony.luck, mchehab,
	bp, robh+dt, linux-kernel
  Cc: kbuild-all, devicetree, openbmc, benjaminfair, yuenn, venture,
	KWLIU, YSCHU, JJLIU0, KFTING, avifishman70, tmaimon77,
	tali.perry1, ctcchien, kflin, Marvin Lin

Hi Marvin,

I love your patch! Perhaps something to improve:

[auto build test WARNING on ras/edac-for-next]
[also build test WARNING on robh/for-next linus/master v6.0-rc2 next-20220826]
[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/Marvin-Lin/EDAC-nuvoton-Add-NPCM-memory-controller-driver/20220816-192514
base:   https://git.kernel.org/pub/scm/linux/kernel/git/ras/ras.git edac-for-next
config: riscv-allyesconfig (https://download.01.org/0day-ci/archive/20220826/202208262327.xR9E62aK-lkp@intel.com/config)
compiler: riscv64-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/869724e680023a78ccf48489cf12da04e9317347
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Marvin-Lin/EDAC-nuvoton-Add-NPCM-memory-controller-driver/20220816-192514
        git checkout 869724e680023a78ccf48489cf12da04e9317347
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=riscv SHELL=/bin/bash drivers/edac/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   drivers/edac/npcm_edac.c: In function 'force_ecc_error':
>> drivers/edac/npcm_edac.c:192:44: warning: initialization discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
     192 |         struct npcm_platform_data *pdata = priv->pdata;
         |                                            ^~~~
   drivers/edac/npcm_edac.c: In function 'handle_ce':
>> drivers/edac/npcm_edac.c:107:23: warning: 'addr' is used uninitialized [-Wuninitialized]
     107 |         addr = ((addr | val_h) << 32) | val_l;
         |                 ~~~~~~^~~~~~~~
   drivers/edac/npcm_edac.c:99:13: note: 'addr' was declared here
      99 |         u64 addr, data;
         |             ^~~~
>> drivers/edac/npcm_edac.c:112:23: warning: 'data' is used uninitialized [-Wuninitialized]
     112 |         data = ((data | val_h) << 32) | val_l;
         |                 ~~~~~~^~~~~~~~
   drivers/edac/npcm_edac.c:99:19: note: 'data' was declared here
      99 |         u64 addr, data;
         |                   ^~~~
   drivers/edac/npcm_edac.c: In function 'handle_ue':
   drivers/edac/npcm_edac.c:139:23: warning: 'addr' is used uninitialized [-Wuninitialized]
     139 |         addr = ((addr | val_h) << 32) | val_l;
         |                 ~~~~~~^~~~~~~~
   drivers/edac/npcm_edac.c:131:13: note: 'addr' was declared here
     131 |         u64 addr, data;
         |             ^~~~
   drivers/edac/npcm_edac.c:144:23: warning: 'data' is used uninitialized [-Wuninitialized]
     144 |         data = ((data | val_h) << 32) | val_l;
         |                 ~~~~~~^~~~~~~~
   drivers/edac/npcm_edac.c:131:19: note: 'data' was declared here
     131 |         u64 addr, data;
         |                   ^~~~


vim +/const +192 drivers/edac/npcm_edac.c

    94	
    95	static void handle_ce(struct mem_ctl_info *mci)
    96	{
    97		struct priv_data *priv = mci->pvt_info;
    98		const struct npcm_platform_data *pdata = priv->pdata;
    99		u64 addr, data;
   100		u32 val_l, val_h, id, synd;
   101	
   102		regmap_read(npcm_regmap, pdata->ctl_ce_addr_l, &val_l);
   103		if (pdata->chip == NPCM8XX_CHIP) {
   104			regmap_read(npcm_regmap, pdata->ctl_ce_addr_h, &val_h);
   105			val_h &= pdata->ce_addr_h_mask;
   106		}
 > 107		addr = ((addr | val_h) << 32) | val_l;
   108	
   109		regmap_read(npcm_regmap, pdata->ctl_ce_data_l, &val_l);
   110		if (pdata->chip == NPCM8XX_CHIP)
   111			regmap_read(npcm_regmap, pdata->ctl_ce_data_h, &val_h);
 > 112		data = ((data | val_h) << 32) | val_l;
   113	
   114		regmap_read(npcm_regmap, pdata->ctl_source_id, &id);
   115		id = (id & pdata->source_id_ce_mask) >> pdata->source_id_ce_shift;
   116	
   117		regmap_read(npcm_regmap, pdata->ctl_ce_synd, &synd);
   118		synd = (synd & pdata->ce_synd_mask) >> pdata->ce_synd_shift;
   119	
   120		snprintf(priv->message, EDAC_MSG_SIZE,
   121			 "addr = 0x%llx, data = 0x%llx, id = 0x%x", addr, data, id);
   122	
   123		edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, addr >> PAGE_SHIFT,
   124				     addr & ~PAGE_MASK, synd, 0, 0, -1, priv->message, "");
   125	}
   126	
   127	static void handle_ue(struct mem_ctl_info *mci)
   128	{
   129		struct priv_data *priv = mci->pvt_info;
   130		const struct npcm_platform_data *pdata = priv->pdata;
   131		u64 addr, data;
   132		u32 val_l, val_h, id, synd;
   133	
   134		regmap_read(npcm_regmap, pdata->ctl_ue_addr_l, &val_l);
   135		if (pdata->chip == NPCM8XX_CHIP) {
   136			regmap_read(npcm_regmap, pdata->ctl_ue_addr_h, &val_h);
   137			val_h &= pdata->ue_addr_h_mask;
   138		}
   139		addr = ((addr | val_h) << 32) | val_l;
   140	
   141		regmap_read(npcm_regmap, pdata->ctl_ue_data_l, &val_l);
   142		if (pdata->chip == NPCM8XX_CHIP)
   143			regmap_read(npcm_regmap, pdata->ctl_ue_data_h, &val_h);
   144		data = ((data | val_h) << 32) | val_l;
   145	
   146		regmap_read(npcm_regmap, pdata->ctl_source_id, &id);
   147		id = (id & pdata->source_id_ue_mask) >> pdata->source_id_ue_shift;
   148	
   149		regmap_read(npcm_regmap, pdata->ctl_ue_synd, &synd);
   150		synd = (synd & pdata->ue_synd_mask) >> pdata->ue_synd_shift;
   151	
   152		snprintf(priv->message, EDAC_MSG_SIZE,
   153			 "addr = 0x%llx, data = 0x%llx, id = 0x%x", addr, data, id);
   154	
   155		edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, addr >> PAGE_SHIFT,
   156				     addr & ~PAGE_MASK, synd, 0, 0, -1, priv->message, "");
   157	}
   158	
   159	static irqreturn_t edac_ecc_isr(int irq, void *dev_id)
   160	{
   161		struct mem_ctl_info *mci = dev_id;
   162		struct priv_data *priv = mci->pvt_info;
   163		const struct npcm_platform_data *pdata = priv->pdata;
   164		u32 status;
   165	
   166		regmap_read(npcm_regmap, pdata->ctl_int_status, &status);
   167		if (status & pdata->int_status_ce_mask) {
   168			handle_ce(mci);
   169	
   170			/* acknowledge the CE interrupt */
   171			regmap_write(npcm_regmap, pdata->ctl_int_ack,
   172				     pdata->int_ack_ce_mask);
   173			return IRQ_HANDLED;
   174		} else if (status & pdata->int_status_ue_mask) {
   175			handle_ue(mci);
   176	
   177			/* acknowledge the UE interrupt */
   178			regmap_write(npcm_regmap, pdata->ctl_int_ack,
   179				     pdata->int_ack_ue_mask);
   180			return IRQ_HANDLED;
   181		}
   182	
   183		return IRQ_NONE;
   184	}
   185	
   186	static ssize_t force_ecc_error(struct file *file, const char __user *data,
   187					    size_t count, loff_t *ppos)
   188	{
   189		struct device *dev = file->private_data;
   190		struct mem_ctl_info *mci = to_mci(dev);
   191		struct priv_data *priv = mci->pvt_info;
 > 192		struct npcm_platform_data *pdata = priv->pdata;
   193		int ret;
   194		u32 val, syndrome;
   195	
   196		/*
   197		 * error_type - 0: CE, 1: UE
   198		 * location   - 0: data, 1: checkcode
   199		 * bit        - 0 ~ 63 for data and 0 ~ 7 for checkcode
   200		 */
   201		edac_printk(KERN_INFO, EDAC_MOD_NAME,
   202			    "force an ECC error, type = %d, location = %d, bit = %d\n",
   203			    priv->error_type, priv->location, priv->bit);
   204	
   205		/* ensure no pending writes */
   206		ret = regmap_read_poll_timeout(npcm_regmap, pdata->ctl_controller_busy,
   207					       val, !(val & pdata->controller_busy_mask),
   208					       1000, 10000);
   209		if (ret) {
   210			edac_printk(KERN_INFO, EDAC_MOD_NAME,
   211				    "wait pending writes timeout\n");
   212			return count;
   213		}
   214	
   215		regmap_read(npcm_regmap, pdata->ctl_xor_check_bits, &val);
   216		val &= ~pdata->xor_check_bits_mask;
   217	
   218		/* write syndrome to XOR_CHECK_BITS */
   219		if (priv->error_type == 0) {
   220			if (priv->location == 0 && priv->bit > 63) {
   221				edac_printk(KERN_INFO, EDAC_MOD_NAME,
   222					    "data bit should not exceed 63\n");
   223				return count;
   224			}
   225	
   226			if (priv->location == 1 && priv->bit > 7) {
   227				edac_printk(KERN_INFO, EDAC_MOD_NAME,
   228					    "checkcode bit should not exceed 7\n");
   229				return count;
   230			}
   231	
   232			syndrome = priv->location ? 1 << priv->bit :
   233				   data_synd[priv->bit];
   234	
   235			regmap_write(npcm_regmap, pdata->ctl_xor_check_bits,
   236				     val | (syndrome << pdata->xor_check_bits_shift) |
   237				     pdata->writeback_en_mask);
   238		} else if (priv->error_type == 1) {
   239			regmap_write(npcm_regmap, pdata->ctl_xor_check_bits,
   240				     val | (UE_SYNDROME << pdata->xor_check_bits_shift));
   241		}
   242	
   243		/* force write check */
   244		regmap_update_bits(npcm_regmap, pdata->ctl_xor_check_bits,
   245				   pdata->fwc_mask, pdata->fwc_mask);
   246	
   247		return count;
   248	}
   249	

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

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

* Re: [PATCH v13 2/3] dt-bindings: edac: nuvoton: Add document for NPCM memory controller
  2022-08-18 14:44   ` Rob Herring
@ 2022-08-29  3:52     ` Kun-Fa Lin
  0 siblings, 0 replies; 7+ messages in thread
From: Kun-Fa Lin @ 2022-08-29  3:52 UTC (permalink / raw)
  To: Rob Herring
  Cc: linux-edac, rric, James Morse, tony.luck, Mauro Carvalho Chehab,
	Borislav Petkov, Linux Kernel Mailing List, CS20 KWLiu, YSCHU,
	Benjamin Fair, devicetree, Avi Fishman, Patrick Venture,
	OpenBMC Maillist, KFTING, JJLIU0, Tali Perry, ctcchien,
	Marvin Lin, Tomer Maimon

Hi Rob,

Thanks for the review.

Regards,
Marvin

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

end of thread, other threads:[~2022-08-29  3:53 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-16  9:46 [PATCH v13 0/3] EDAC/nuvoton: Add NPCM memory controller driver Marvin Lin
2022-08-16  9:46 ` [PATCH v13 1/3] arm: dts: nuvoton: Add node for NPCM memory controller Marvin Lin
2022-08-16  9:46 ` [PATCH v13 2/3] dt-bindings: edac: nuvoton: Add document " Marvin Lin
2022-08-18 14:44   ` Rob Herring
2022-08-29  3:52     ` Kun-Fa Lin
2022-08-16  9:46 ` [PATCH v13 3/3] EDAC/nuvoton: Add NPCM memory controller driver Marvin Lin
2022-08-26 15:59   ` kernel test robot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).