]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
EDAC/synopsys: Add support for version 3 of the Synopsys EDAC DDR
authorDinh Nguyen <dinguyen@kernel.org>
Tue, 12 Oct 2021 19:07:07 +0000 (14:07 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 17 Oct 2024 13:10:42 +0000 (15:10 +0200)
[ Upstream commit f7824ded41491d7ebc156a3a2f6fa05cd89da7c2 ]

Add support for version 3.80a of the Synopsys DDR controller. This
version of the controller has the following differences:

- UE/CE are auto cleared
- Interrupts are supported by default

Signed-off-by: Dinh Nguyen <dinguyen@kernel.org>
Signed-off-by: Borislav Petkov <bp@suse.de>
Reviewed-by: Michal Simek <michal.simek@xilinx.com>
Link: https://lkml.kernel.org/r/20211012190709.1504152-2-dinguyen@kernel.org
Stable-dep-of: 35e6dbfe1846 ("EDAC/synopsys: Fix error injection on Zynq UltraScale+")
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/edac/synopsys_edac.c

index 8557781bb8dceeda6d6a27992ea32a26b8b8e0a4..40b1abeca8562e2b55dde35aa488e86beb2e9ee4 100644 (file)
 /* DDR ECC Quirks */
 #define DDR_ECC_INTR_SUPPORT           BIT(0)
 #define DDR_ECC_DATA_POISON_SUPPORT    BIT(1)
+#define DDR_ECC_INTR_SELF_CLEAR                BIT(2)
 
 /* ZynqMP Enhanced DDR memory controller registers that are relevant to ECC */
 /* ECC Configuration Registers */
 #define DDR_QOS_IRQ_EN_OFST            0x20208
 #define DDR_QOS_IRQ_DB_OFST            0x2020C
 
+/* DDR QOS Interrupt register definitions */
+#define DDR_UE_MASK                    BIT(9)
+#define DDR_CE_MASK                    BIT(8)
+
 /* ECC Corrected Error Register Mask and Shifts*/
 #define ECC_CEADDR0_RW_MASK            0x3FFFF
 #define ECC_CEADDR0_RNK_MASK           BIT(24)
@@ -539,10 +544,16 @@ static irqreturn_t intr_handler(int irq, void *dev_id)
        priv = mci->pvt_info;
        p_data = priv->p_data;
 
-       regval = readl(priv->baseaddr + DDR_QOS_IRQ_STAT_OFST);
-       regval &= (DDR_QOSCE_MASK | DDR_QOSUE_MASK);
-       if (!(regval & ECC_CE_UE_INTR_MASK))
-               return IRQ_NONE;
+       /*
+        * v3.0 of the controller has the ce/ue bits cleared automatically,
+        * so this condition does not apply.
+        */
+       if (!(priv->p_data->quirks & DDR_ECC_INTR_SELF_CLEAR)) {
+               regval = readl(priv->baseaddr + DDR_QOS_IRQ_STAT_OFST);
+               regval &= (DDR_QOSCE_MASK | DDR_QOSUE_MASK);
+               if (!(regval & ECC_CE_UE_INTR_MASK))
+                       return IRQ_NONE;
+       }
 
        status = p_data->get_error_info(priv);
        if (status)
@@ -554,7 +565,9 @@ static irqreturn_t intr_handler(int irq, void *dev_id)
 
        edac_dbg(3, "Total error count CE %d UE %d\n",
                 priv->ce_cnt, priv->ue_cnt);
-       writel(regval, priv->baseaddr + DDR_QOS_IRQ_STAT_OFST);
+       /* v3.0 of the controller does not have this register */
+       if (!(priv->p_data->quirks & DDR_ECC_INTR_SELF_CLEAR))
+               writel(regval, priv->baseaddr + DDR_QOS_IRQ_STAT_OFST);
        return IRQ_HANDLED;
 }
 
@@ -840,8 +853,13 @@ static void mc_init(struct mem_ctl_info *mci, struct platform_device *pdev)
 static void enable_intr(struct synps_edac_priv *priv)
 {
        /* Enable UE/CE Interrupts */
-       writel(DDR_QOSUE_MASK | DDR_QOSCE_MASK,
-                       priv->baseaddr + DDR_QOS_IRQ_EN_OFST);
+       if (priv->p_data->quirks & DDR_ECC_INTR_SELF_CLEAR)
+               writel(DDR_UE_MASK | DDR_CE_MASK,
+                      priv->baseaddr + ECC_CLR_OFST);
+       else
+               writel(DDR_QOSUE_MASK | DDR_QOSCE_MASK,
+                      priv->baseaddr + DDR_QOS_IRQ_EN_OFST);
+
 }
 
 static void disable_intr(struct synps_edac_priv *priv)
@@ -896,6 +914,19 @@ static const struct synps_platform_data zynqmp_edac_def = {
                          ),
 };
 
+static const struct synps_platform_data synopsys_edac_def = {
+       .get_error_info = zynqmp_get_error_info,
+       .get_mtype      = zynqmp_get_mtype,
+       .get_dtype      = zynqmp_get_dtype,
+       .get_ecc_state  = zynqmp_get_ecc_state,
+       .quirks         = (DDR_ECC_INTR_SUPPORT | DDR_ECC_INTR_SELF_CLEAR
+#ifdef CONFIG_EDAC_DEBUG
+                         | DDR_ECC_DATA_POISON_SUPPORT
+#endif
+                         ),
+};
+
+
 static const struct of_device_id synps_edac_match[] = {
        {
                .compatible = "xlnx,zynq-ddrc-a05",
@@ -905,6 +936,10 @@ static const struct of_device_id synps_edac_match[] = {
                .compatible = "xlnx,zynqmp-ddrc-2.40a",
                .data = (void *)&zynqmp_edac_def
        },
+       {
+               .compatible = "snps,ddrc-3.80a",
+               .data = (void *)&synopsys_edac_def
+       },
        {
                /* end of table */
        }