* [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).